/* * Copyright (c) 2006-2016, openmetaverse.co * Copyright (c) 2024-2025, Sjofn LLC * All rights reserved. * * - Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Neither the name of the openmetaverse.co nor the names * of its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace OpenMetaverse { /// /// The LockingDictionary class is used through the library for storing key/value pairs. /// It is intended to be a replacement for the generic Dictionary class and should /// be used in its place. It contains several methods for allowing access to the data from /// outside the library that are read only and thread safe. /// /// Key /// Value public class LockingDictionary : IDictionary { /// /// Internal dictionary that this class wraps around. Do not /// modify or enumerate the contents of this dictionary without locking /// on this member /// internal Dictionary Dictionary; public Dictionary Copy() { lock (Dictionary) return new Dictionary(Dictionary); } public void Add(KeyValuePair item) { lock (Dictionary) Dictionary.Add(item.Key, item.Value); } public void Clear() { lock (Dictionary) Dictionary.Clear(); } public bool Contains(KeyValuePair item) { TValue v; lock (Dictionary) return (Dictionary.TryGetValue(item.Key, out v) && v.Equals(item.Key)); } public void CopyTo(KeyValuePair[] array, int arrayIndex) { lock (Dictionary) ((ICollection>)Dictionary).CopyTo(array, arrayIndex); } public bool Remove(KeyValuePair item) { if (!Contains(item)) { return false; } lock (Dictionary) Dictionary.Remove(item.Key); return true; } public void OnDeserialization(object sender) { lock( Dictionary) Dictionary.OnDeserialization(sender); } public IEqualityComparer Comparer { get { lock (Dictionary) return Dictionary.Comparer; } } /// /// Gets the number of Key/Value pairs contained in /// public int Count { get { lock (Dictionary) return Dictionary.Count; } } public bool IsReadOnly { get; private set; } /// /// Initializes a new instance of Class /// with the specified key/value, has the default initial capacity. /// public LockingDictionary() { Dictionary = new Dictionary(); } /// /// Initializes a new instance of the Class /// with the specified key/value, has its initial values copied from the specified /// /// /// /// to copy initial values from /// /// /// // initialize a new LockingDictionary named testAvName with a UUID as the key and an string as the value. /// // populates with copied values from example KeyNameCache Dictionary. /// /// // create source dictionary /// Dictionary<UUID, string> KeyNameCache = new Dictionary<UUID, string>(); /// KeyNameCache.Add("8300f94a-7970-7810-cf2c-fc9aa6cdda24", "Jack Avatar"); /// KeyNameCache.Add("27ba1e40-13f7-0708-3e98-5819d780bd62", "Jill Avatar"); /// /// // Initialize new dictionary. /// public LockingDictionary<UUID, string> testAvName = new LockingDictionary<UUID, string>(KeyNameCache); /// /// public LockingDictionary(IDictionary dictionary) { Dictionary = new Dictionary(dictionary); } /// /// Initializes a new instance of /// with the specified key/value, With its initial capacity specified. /// /// Initial size of dictionary /// /// /// // initialize a new LockingDictionary named testDict with a string as the key and an int as the value, /// // initially allocated room for 10 entries. /// public LockingDictionary<string, int> testDict = new LockingDictionary<string, int>(10); /// /// public LockingDictionary(int capacity) { Dictionary = new Dictionary(capacity); } bool IDictionary.Remove(TKey key) { return Remove(key); } /// /// Try to get entry from with specified key /// /// Key to use for lookup /// Value returned /// if specified key exists, if not found public bool TryGetValue(TKey key, out TValue value) { lock (Dictionary) { return Dictionary.TryGetValue(key, out value); } } /// /// Finds the specified match. /// /// The match. /// Matched value /// /// /// // use a delegate to find a prim in the ObjectsPrimitives LockingDictionary /// // with the ID 95683496 /// uint findID = 95683496; /// Primitive findPrim = sim.ObjectsPrimitives.Find( /// delegate(Primitive prim) { return prim.ID == findID; }); /// /// public TValue Find(Predicate match) { lock (Dictionary) { foreach (var value in Dictionary.Values.Where(value => match(value))) { return value; } } return default(TValue); } /// Find All items in /// return matching items. /// a containing found items. /// /// Find All prims within 20 meters and store them in a List /// /// int radius = 20; /// List<Primitive> prims = Client.Network.CurrentSim.ObjectsPrimitives.FindAll( /// delegate(Primitive prim) { /// Vector3 pos = prim.Position; /// return ((prim.ParentID == 0) && (pos != Vector3.Zero) && (Vector3.Distance(pos, location) < radius)); /// } /// ); /// /// public List FindAll(Predicate match) { var found = new List(); lock (Dictionary) { found.AddRange(from kvp in Dictionary where match(kvp.Value) select kvp.Value); } return found; } /// Find All items in /// return matching keys. /// a containing found keys. /// /// Find All keys which also exist in another dictionary /// /// List<UUID> matches = myDict.FindAll( /// delegate(UUID id) { /// return myOtherDict.ContainsKey(id); /// } /// ); /// /// public List FindAll(Predicate match) { var found = new List(); lock (Dictionary) { found.AddRange(from kvp in Dictionary where match(kvp.Key) select kvp.Key); } return found; } /// Perform an on each entry in /// to perform /// /// /// // Iterates over the ObjectsPrimitives LockingDictionary and prints out some information. /// Client.Network.CurrentSim.ObjectsPrimitives.ForEach( /// delegate(Primitive prim) /// { /// if (prim.Text != null) /// { /// Console.WriteLine("NAME={0} ID = {1} TEXT = '{2}'", /// prim.PropertiesFamily.Name, prim.ID, prim.Text); /// } /// }); /// /// public void ForEach(Action action) { lock (Dictionary) { foreach (var value in Dictionary.Values) { action(value); } } } /// Perform on each key of /// to perform public void ForEach(Action action) { lock (Dictionary) { foreach (var key in Dictionary.Keys) { action(key); } } } /// /// Perform an on each KeyValuePair of /// /// to perform public void ForEach(Action> action) { lock (Dictionary) { foreach (var entry in Dictionary) { action(entry); } } } void IDictionary.Add(TKey key, TValue value) { Add(key, value); } /// Check if Key exists in Dictionary /// Key to check for /// if found, otherwise public bool ContainsKey(TKey key) { lock (Dictionary) return Dictionary.ContainsKey(key); } /// Check if Value exists in Dictionary /// Value to check for /// if found, otherwise public bool ContainsValue(TValue value) { lock (Dictionary) return Dictionary.ContainsValue(value); } /// /// Adds the specified key to the dictionary, dictionary locking is not performed, /// /// /// The key /// The value internal void Add(TKey key, TValue value) { lock (Dictionary) Dictionary[key] = value; } /// /// Removes the specified key, dictionary locking is not performed /// /// The key. /// if successful, otherwise internal bool Remove(TKey key) { lock (Dictionary) return Dictionary.Remove(key); } /// /// Indexer for the dictionary /// /// The key /// The value public TValue this[TKey key] { get { lock (Dictionary) { return Dictionary[key]; } } set { lock (Dictionary) { Dictionary[key] = value; } } } public object this[object key] { get { lock (Dictionary) { return Dictionary[(TKey) key]; } } set { lock (Dictionary) { Dictionary[(TKey) key] = (TValue) value; } } } /// public ICollection Keys { get { lock (Dictionary) { return Dictionary.Keys; } } } /// public ICollection Values { get { lock (Dictionary) { return Dictionary.Values; } } } /// public IEnumerator> GetEnumerator() { lock (Dictionary) { return Dictionary.GetEnumerator(); } } /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } }