aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/XMREngine/XMRArray.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRArray.cs534
1 files changed, 534 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRArray.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRArray.cs
new file mode 100644
index 0000000..36d95d3
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRArray.cs
@@ -0,0 +1,534 @@
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
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Text;
32
33using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
34using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
35using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
36using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
37using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
38using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
39using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
40
41// This class exists in the main app domain
42//
43namespace OpenSim.Region.ScriptEngine.XMREngine
44{
45 /**
46 * @brief Array objects.
47 */
48 public class XMR_Array {
49 private const int EMPTYHEAP = 64;
50 private const int ENTRYHEAP = 24;
51
52 private bool enumrValid; // true: enumr set to return array[arrayValid]
53 // false: array[0..arrayValid-1] is all there is
54 private SortedDictionary<object, object> dnary;
55 private SortedDictionary<object, object>.Enumerator enumr;
56 // enumerator used to fill 'array' past arrayValid to end of dictionary
57 private int arrayValid; // number of elements in 'array' that have been filled in
58 private KeyValuePair<object, object>[] array; // list of kvp's that have been returned by ForEach() since last modification
59 private XMRInstAbstract inst; // script instance debited with heap use
60 private int heapUse; // current heap use debit amount
61
62 public static TokenTypeSDTypeDelegate countDelegate = new TokenTypeSDTypeDelegate (new TokenTypeInt (null), new TokenType[0]);
63 public static TokenTypeSDTypeDelegate clearDelegate = new TokenTypeSDTypeDelegate (new TokenTypeVoid (null), new TokenType[0]);
64 public static TokenTypeSDTypeDelegate indexDelegate = new TokenTypeSDTypeDelegate (new TokenTypeObject (null), new TokenType[] { new TokenTypeInt (null) });
65 public static TokenTypeSDTypeDelegate valueDelegate = new TokenTypeSDTypeDelegate (new TokenTypeObject (null), new TokenType[] { new TokenTypeInt (null) });
66
67 public XMR_Array (XMRInstAbstract inst)
68 {
69 this.inst = inst;
70 dnary = new SortedDictionary<object, object> (XMRArrayKeyComparer.singleton);
71 heapUse = inst.UpdateHeapUse (0, EMPTYHEAP);
72 }
73
74 ~XMR_Array ()
75 {
76 heapUse = inst.UpdateHeapUse (heapUse, 0);
77 }
78
79 public static TokenType GetRValType (TokenName name)
80 {
81 if (name.val == "count") return new TokenTypeInt (name);
82 if (name.val == "clear") return clearDelegate;
83 if (name.val == "index") return indexDelegate;
84 if (name.val == "value") return valueDelegate;
85 return new TokenTypeVoid (name);
86 }
87
88 /**
89 * @brief Handle 'array[index]' syntax to get or set an element of the dictionary.
90 * Get returns null if element not defined, script sees type 'undef'.
91 * Setting an element to null removes it.
92 */
93 public object GetByKey(object key)
94 {
95 object val;
96 key = FixKey (key);
97 if (!dnary.TryGetValue (key, out val)) val = null;
98 return val;
99 }
100
101 public void SetByKey(object key, object value)
102 {
103 key = FixKey (key);
104
105 /*
106 * Update heap use throwing an exception on failure
107 * before making any changes to the array.
108 */
109 int keysize = HeapTrackerObject.Size (key);
110 int newheapuse = heapUse;
111 object oldval;
112 if (dnary.TryGetValue (key, out oldval)) {
113 newheapuse -= keysize + HeapTrackerObject.Size (oldval);
114 }
115 if (value != null) {
116 newheapuse += keysize + HeapTrackerObject.Size (value);
117 }
118 heapUse = inst.UpdateHeapUse (heapUse, newheapuse);
119
120 /*
121 * Save new value in array, replacing one of same key if there.
122 * null means remove the value, ie, script did array[key] = undef.
123 */
124 if (value != null) {
125 dnary[key] = value;
126 } else {
127 dnary.Remove (key);
128
129 /*
130 * Shrink the enumeration array, but always leave at least one element.
131 */
132 if ((array != null) && (dnary.Count < array.Length / 2)) {
133 Array.Resize<KeyValuePair<object, object>> (ref array, array.Length / 2);
134 }
135 }
136
137 /*
138 * The enumeration array is invalid because the dictionary has been modified.
139 * Next time a ForEach() call happens, it will repopulate 'array' as elements are retrieved.
140 */
141 arrayValid = 0;
142 }
143
144 /**
145 * @brief Converts an 'object' type to array, key, list, string, but disallows null,
146 * as our language doesn't allow types other than 'object' to be null.
147 * Value types (float, rotation, etc) don't need explicit check for null as
148 * the C# runtime can't convert a null to a value type, and throws an exception.
149 * But for any reference type (array, key, etc) we must manually check for null.
150 */
151 public static XMR_Array Obj2Array (object obj)
152 {
153 if (obj == null) throw new NullReferenceException ();
154 return (XMR_Array)obj;
155 }
156 public static LSL_Key Obj2Key (object obj)
157 {
158 if (obj == null) throw new NullReferenceException ();
159 return (LSL_Key)obj;
160 }
161 public static LSL_List Obj2List (object obj)
162 {
163 if (obj == null) throw new NullReferenceException ();
164 return (LSL_List)obj;
165 }
166 public static LSL_String Obj2String (object obj)
167 {
168 if (obj == null) throw new NullReferenceException ();
169 return obj.ToString ();
170 }
171
172 /**
173 * @brief remove all elements from the array.
174 * sets everything to its 'just constructed' state.
175 */
176 public void __pub_clear ()
177 {
178 heapUse = inst.UpdateHeapUse (heapUse, EMPTYHEAP);
179 dnary.Clear ();
180 enumrValid = false;
181 arrayValid = 0;
182 array = null;
183 }
184
185 /**
186 * @brief return number of elements in the array.
187 */
188 public int __pub_count ()
189 {
190 return dnary.Count;
191 }
192
193 /**
194 * @brief Retrieve index (key) of an arbitrary element.
195 * @param number = number of the element (0 based)
196 * @returns null: array doesn't have that many elements
197 * else: index (key) for that element
198 */
199 public object __pub_index (int number)
200 {
201 return ForEach (number) ? UnfixKey (array[number].Key) : null;
202 }
203
204 /**
205 * @brief Retrieve value of an arbitrary element.
206 * @param number = number of the element (0 based)
207 * @returns null: array doesn't have that many elements
208 * else: value for that element
209 */
210 public object __pub_value (int number)
211 {
212 return ForEach (number) ? array[number].Value : null;
213 }
214
215 /**
216 * @brief Called in each iteration of a 'foreach' statement.
217 * @param number = index of element to retrieve (0 = first one)
218 * @returns false: element does not exist
219 * true: element exists
220 */
221 private bool ForEach (int number)
222 {
223 /*
224 * If we don't have any array, we can't have ever done
225 * any calls here before, so allocate an array big enough
226 * and set everything else to the beginning.
227 */
228 if (array == null) {
229 array = new KeyValuePair<object, object>[dnary.Count];
230 arrayValid = 0;
231 }
232
233 /*
234 * If dictionary modified since last enumeration, get a new enumerator.
235 */
236 if (arrayValid == 0) {
237 enumr = dnary.GetEnumerator ();
238 enumrValid = true;
239 }
240
241 /*
242 * Make sure we have filled the array up enough for requested element.
243 */
244 while ((arrayValid <= number) && enumrValid && enumr.MoveNext ()) {
245 if (arrayValid >= array.Length) {
246 Array.Resize<KeyValuePair<object, object>> (ref array, dnary.Count);
247 }
248 array[arrayValid++] = enumr.Current;
249 }
250
251 /*
252 * If we don't have that many elements, return end-of-array status.
253 */
254 return number < arrayValid;
255 }
256
257 /**
258 * @brief Transmit array out in such a way that it can be reconstructed,
259 * including any in-progress ForEach() enumerations.
260 */
261 public delegate void SendArrayObjDelegate (object graph);
262 public void SendArrayObj (SendArrayObjDelegate sendObj)
263 {
264 /*
265 * Set the count then the elements themselves.
266 * UnfixKey() because sendObj doesn't handle XMRArrayListKeys.
267 */
268 sendObj (dnary.Count);
269 foreach (KeyValuePair<object, object> kvp in dnary) {
270 sendObj (UnfixKey (kvp.Key));
271 sendObj (kvp.Value);
272 }
273 }
274
275 /**
276 * @brief Receive array in. Any previous contents are erased.
277 * Set up such that any enumeration in progress will resume
278 * at the exact spot and in the exact same order as they
279 * were in on the sending side.
280 */
281 public delegate object RecvArrayObjDelegate ();
282 public void RecvArrayObj (RecvArrayObjDelegate recvObj)
283 {
284 heapUse = inst.UpdateHeapUse (heapUse, EMPTYHEAP);
285
286 /*
287 * Cause any enumeration to refill the array from the sorted dictionary.
288 * Since it is a sorted dictionary, any enumerations will be in the same
289 * order as on the sending side.
290 */
291 arrayValid = 0;
292 enumrValid = false;
293
294 /*
295 * Fill dictionary.
296 */
297 dnary.Clear ();
298 int count = (int)recvObj ();
299 while (-- count >= 0) {
300 object key = FixKey (recvObj ());
301 object val = recvObj ();
302 int htuse = HeapTrackerObject.Size (key) + HeapTrackerObject.Size (val);
303 heapUse = inst.UpdateHeapUse (heapUse, heapUse + htuse);
304 dnary.Add (key, val);
305 }
306 }
307
308 /**
309 * We want our index values to be of consistent type, otherwise we get things like (LSL_Integer)1 != (int)1.
310 * So strip off any LSL-ness from the types.
311 * We also deep-strip any given lists used as keys (multi-dimensional arrays).
312 */
313 public static object FixKey (object key)
314 {
315 if (key is LSL_Integer) return (int)(LSL_Integer)key;
316 if (key is LSL_Float) return (double)(LSL_Float)key;
317 if (key is LSL_Key) return (string)(LSL_Key)key;
318 if (key is LSL_String) return (string)(LSL_String)key;
319 if (key is LSL_List) {
320 object[] data = ((LSL_List)key).Data;
321 if (data.Length == 1) return FixKey (data[0]);
322 return new XMRArrayListKey ((LSL_List)key);
323 }
324 return key; // int, double, string, LSL_Vector, LSL_Rotation, etc are ok as is
325 }
326
327 /**
328 * @brief When returning a key, such as for array.index(), we want to return the original
329 * LSL_List, not the sanitized one, as the script compiler expects an LSL_List.
330 * Any other sanitized types can remain as is (int, string, etc).
331 */
332 private static object UnfixKey (object key)
333 {
334 if (key is XMRArrayListKey) key = ((XMRArrayListKey)key).GetOriginal ();
335 return key;
336 }
337 }
338
339 public class XMRArrayKeyComparer : IComparer<object> {
340
341 public static XMRArrayKeyComparer singleton = new XMRArrayKeyComparer ();
342
343 /**
344 * @brief Compare two keys
345 */
346 public int Compare (object x, object y) // IComparer<object>
347 {
348 /*
349 * Use short type name (eg, String, Int32, XMRArrayListKey) as most significant part of key.
350 */
351 string xtn = x.GetType ().Name;
352 string ytn = y.GetType ().Name;
353 int ctn = String.CompareOrdinal (xtn, ytn);
354 if (ctn != 0) return ctn;
355
356 ComparerDelegate cd;
357 if (!comparers.TryGetValue (xtn, out cd)) {
358 throw new Exception ("unsupported key type " + xtn);
359 }
360 return cd (x, y);
361 }
362
363 private delegate int ComparerDelegate (object a, object b);
364
365 private static Dictionary<string, ComparerDelegate> comparers = BuildComparers ();
366
367 private static Dictionary<string, ComparerDelegate> BuildComparers ()
368 {
369 Dictionary<string, ComparerDelegate> cmps = new Dictionary<string, ComparerDelegate> ();
370 cmps.Add (typeof (double).Name, MyFloatComparer);
371 cmps.Add (typeof (int).Name, MyIntComparer);
372 cmps.Add (typeof (XMRArrayListKey).Name, MyListKeyComparer);
373 cmps.Add (typeof (LSL_Rotation).Name, MyRotationComparer);
374 cmps.Add (typeof (string).Name, MyStringComparer);
375 cmps.Add (typeof (LSL_Vector).Name, MyVectorComparer);
376 return cmps;
377 }
378
379 private static int MyFloatComparer (object a, object b)
380 {
381 double af = (double)a;
382 double bf = (double)b;
383 if (af < bf) return -1;
384 if (af > bf) return 1;
385 return 0;
386 }
387 private static int MyIntComparer (object a, object b)
388 {
389 return (int)a - (int)b;
390 }
391 private static int MyListKeyComparer (object a, object b)
392 {
393 XMRArrayListKey alk = (XMRArrayListKey)a;
394 XMRArrayListKey blk = (XMRArrayListKey)b;
395 return XMRArrayListKey.Compare (alk, blk);
396 }
397 private static int MyRotationComparer (object a, object b)
398 {
399 LSL_Rotation ar = (LSL_Rotation)a;
400 LSL_Rotation br = (LSL_Rotation)b;
401 if (ar.x < br.x) return -1;
402 if (ar.x > br.x) return 1;
403 if (ar.y < br.y) return -1;
404 if (ar.y > br.y) return 1;
405 if (ar.z < br.z) return -1;
406 if (ar.z > br.z) return 1;
407 if (ar.s < br.s) return -1;
408 if (ar.s > br.s) return 1;
409 return 0;
410 }
411 private static int MyStringComparer (object a, object b)
412 {
413 return String.CompareOrdinal ((string)a, (string)b);
414 }
415 private static int MyVectorComparer (object a, object b)
416 {
417 LSL_Vector av = (LSL_Vector)a;
418 LSL_Vector bv = (LSL_Vector)b;
419 if (av.x < bv.x) return -1;
420 if (av.x > bv.x) return 1;
421 if (av.y < bv.y) return -1;
422 if (av.y > bv.y) return 1;
423 if (av.z < bv.z) return -1;
424 if (av.z > bv.z) return 1;
425 return 0;
426 }
427 }
428
429 /**
430 * @brief Lists used as keys must be sanitized first.
431 * List gets converted to an object[] and each element is converted from LSL_ types to system types where possible.
432 * And we also need an equality operator that compares the values of all elements of the list, not just the lengths.
433 * Note that just like LSL_Lists, we consider these objects to be immutable, so they can be directly used as keys in
434 * the dictionary as they don't ever change.
435 */
436 public class XMRArrayListKey {
437 private LSL_List original;
438 private object[] cleaned;
439 private int length;
440 private int hashCode;
441
442 /**
443 * @brief Construct a sanitized object[] from a list.
444 * Also save the original list in case we need it later.
445 */
446 public XMRArrayListKey (LSL_List key)
447 {
448 original = key;
449 object[] given = key.Data;
450 int len = given.Length;
451 length = len;
452 cleaned = new object[len];
453 int hc = len;
454 for (int i = 0; i < len; i ++) {
455 object v = XMR_Array.FixKey (given[i]);
456 hc += hc + ((hc < 0) ? 1 : 0);
457 hc ^= v.GetHashCode ();
458 cleaned[i] = v;
459 }
460 hashCode = hc;
461 }
462
463 /**
464 * @brief Get heap tracking size.
465 */
466 public int Size {
467 get {
468 return original.Size;
469 }
470 }
471
472 /**
473 * @brief See if the given object is an XMRArrayListKey and every value is equal to our own.
474 */
475 public override bool Equals (object o)
476 {
477 if (!(o is XMRArrayListKey)) return false;
478 XMRArrayListKey a = (XMRArrayListKey)o;
479 int len = a.length;
480 if (len != length) return false;
481 if (a.hashCode != hashCode) return false;
482 for (int i = 0; i < len; i ++) {
483 if (!cleaned[i].Equals (a.cleaned[i])) return false;
484 }
485 return true;
486 }
487
488 /**
489 * @brief Get an hash code.
490 */
491 public override int GetHashCode ()
492 {
493 return hashCode;
494 }
495
496 /**
497 * @brief Compare for key sorting.
498 */
499 public static int Compare (XMRArrayListKey x, XMRArrayListKey y)
500 {
501 int j = x.length - y.length;
502 if (j == 0) {
503 for (int i = 0; i < x.length; i ++) {
504 object xo = x.cleaned[i];
505 object yo = y.cleaned[i];
506 j = XMRArrayKeyComparer.singleton.Compare (xo, yo);
507 if (j != 0) break;
508 }
509 }
510 return j;
511 }
512
513 /**
514 * @brief Get the original LSL_List we were built from.
515 */
516 public LSL_List GetOriginal ()
517 {
518 return original;
519 }
520
521 /**
522 * @brief Debugging
523 */
524 public override string ToString ()
525 {
526 StringBuilder sb = new StringBuilder ();
527 for (int i = 0; i < length; i ++) {
528 if (i > 0) sb.Append (',');
529 sb.Append (cleaned[i].ToString ());
530 }
531 return sb.ToString ();
532 }
533 }
534}