From fbdb6c420f3789c1fa1a6d79894358fe5250c8d9 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Tue, 6 Feb 2024 23:48:07 +0000 Subject: [PATCH] missing file... --- .../Region/Framework/Scenes/LinksetData.cs | 502 ++++++++++++++++++ 1 file changed, 502 insertions(+) create mode 100644 OpenSim/Region/Framework/Scenes/LinksetData.cs diff --git a/OpenSim/Region/Framework/Scenes/LinksetData.cs b/OpenSim/Region/Framework/Scenes/LinksetData.cs new file mode 100644 index 0000000000..de2a142912 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/LinksetData.cs @@ -0,0 +1,502 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * 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. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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 OpenMetaverse; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +//using log4net; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; + +namespace OpenSim.Region.Framework.Scenes +{ + public class LinksetData + { + public int m_MemoryLimit; + public int m_MemoryUsed; + private readonly object linksetDataLock = new(); + public Dictionary Data; + + public LinksetData(int limit) + { + Data = new Dictionary(); + + m_MemoryLimit = limit; + m_MemoryUsed = 0; + } + + public LinksetData(int limit, int used) + { + Data = new Dictionary(); + + m_MemoryLimit = limit; + m_MemoryUsed = used; + } + + public LinksetData Copy() + { + lock (linksetDataLock) + { + var copy = new LinksetData(m_MemoryLimit, m_MemoryUsed); + foreach (var entry in Data) + { + LinksetDataEntry val = entry.Value.Copy(); + copy.Data[entry.Key] = val; + } + return copy; + } + } + + /// + /// Adds or updates a entry to linkset data + /// + /// + /// 0 if the data was successfully added or updated + /// 1 if the data could not be added or updated due to memory + /// 3 if the password did not match + /// 5 if the data is unchanged + /// + public int AddOrUpdate(string key, string value, string pass) + { + int deltaMem; + lock (linksetDataLock) + { + if (Data.TryGetValue(key, out LinksetDataEntry entry)) + { + if (!entry.CheckPassword(pass)) + return 3; + + if (entry.Value == value) + return 5; + + deltaMem = value.Length - entry.Value.Length; + if ((m_MemoryUsed + deltaMem) > m_MemoryLimit) + return 1; + + m_MemoryUsed += deltaMem; + if(m_MemoryUsed < 0) + m_MemoryUsed = 0; + + entry.Value = value; + return 0; + } + + deltaMem = value.Length + key.Length; + if (!string.IsNullOrEmpty(pass)) + deltaMem += pass.Length; + + if ((m_MemoryUsed + deltaMem) > m_MemoryLimit) + return 1; + + m_MemoryUsed += deltaMem; + Data[key] = new LinksetDataEntry() + { + Value = value, + Password = pass + }; + return 0; + } + } + + public int AddOrUpdate(string key, string value) + { + int deltaMem; + lock (linksetDataLock) + { + if (Data.TryGetValue(key, out LinksetDataEntry entry)) + { + if (entry.IsProtected) + return 3; + + if (entry.Value == value) + return 5; + + deltaMem = value.Length - entry.Value.Length; + if ((m_MemoryUsed + deltaMem) > m_MemoryLimit) + return 1; + entry.Value = value; + m_MemoryUsed += deltaMem; + return 0; + } + + deltaMem = value.Length + key.Length; + if ((m_MemoryUsed + deltaMem) > m_MemoryLimit) + return 1; + + m_MemoryUsed += deltaMem; + Data[key] = new LinksetDataEntry() + { + Value = value, + Password = null + }; + return 0; + } + } + + /// + /// Deletes a named key from the key value store + /// + /// The key value we're removing + /// The password for a protected field (or string.Empty if not protected) + /// + /// 0 if successful. + /// 1 if not due to the password. + /// -1 if no such key was found + /// + public int Remove(string key, string pass) + { + lock (linksetDataLock) + { + if (Data.Count <= 0) + return 4; + + if (!Data.TryGetValue(key, out LinksetDataEntry entry)) + return 4; + + if (!entry.CheckPassword(pass)) + return 3; + + Data.Remove(key); + + m_MemoryUsed -= key.Length + entry.Value.Length; + if (!string.IsNullOrEmpty(entry.Password)) + m_MemoryUsed -= entry.Password.Length; + if (m_MemoryUsed < 0) + m_MemoryUsed = 0; + + return 0; + } + } + public int Remove(string key) + { + lock (linksetDataLock) + { + if (Data.Count <= 0) + return 4; + + if(string.IsNullOrEmpty(key)) + return 4; + + if (!Data.TryGetValue(key, out LinksetDataEntry entry)) + return 4; + + if (entry.IsProtected) + return 3; + + Data.Remove(key); + + m_MemoryUsed -= key.Length + entry.Value.Length; + if (m_MemoryUsed < 0) + m_MemoryUsed = 0; + + return 0; + } + } + + public string Get(string key, string pass) + { + lock (linksetDataLock) + { + return (Data.TryGetValue(key, out LinksetDataEntry entry) && entry.CheckPassword(pass)) ? entry.Value : string.Empty; + } + } + + public string Get(string key) + { + lock (linksetDataLock) + { + return (Data.TryGetValue(key, out LinksetDataEntry entry) && entry.IsNotProtected) ? entry.Value : string.Empty; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool HasData() + { + return Data.Count > 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Count() + { + return Data.Count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Free() + { + int free = m_MemoryLimit - m_MemoryUsed; + return free > 0 ? free : 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Used() + { + return m_MemoryUsed; + } + + public string[] RemoveByPattern(string pattern, string pass, out int notDeleted) + { + notDeleted = 0; + List ret; + + lock (linksetDataLock) + { + if (Data.Count <= 0) + return Array.Empty(); + try + { + ret = new List(); + Regex reg = new(pattern, RegexOptions.CultureInvariant, TimeSpan.FromMilliseconds(1)); + + foreach (var kvp in Data) + { + if (reg.IsMatch(kvp.Key)) + { + if (kvp.Value.CheckPassword(pass)) + { + int mem = kvp.Value.Value.Length + kvp.Key.Length; + if(kvp.Value.IsProtected) + mem += kvp.Value.Password.Length; + m_MemoryUsed -= mem; + ret.Add(kvp.Key); + } + else + notDeleted++; + } + } + } + catch + { + notDeleted = 0; + return Array.Empty(); + } + + foreach (string k in ret) + Data.Remove(k); + + if (m_MemoryUsed < 0) + m_MemoryUsed = 0; + return ret.ToArray(); + } + } + public int CountByPattern(string pattern) + { + lock (linksetDataLock) + { + if (Data.Count <= 0) + return 0; + try + { + Regex reg = new(pattern, RegexOptions.CultureInvariant, TimeSpan.FromMilliseconds(1)); + + int ret = 0; + foreach (string k in Data.Keys) + { + if (reg.IsMatch(k)) + ret++; + } + return ret; + } + catch + { + return 0; + } + } + } + + public string[] ListKeysByPatttern(string pattern, int start, int count) + { + List lkeys; + lock (linksetDataLock) + { + if (Data.Count <= 0 || start >= Data.Count) + return Array.Empty(); + try + { + Regex reg = new(pattern, RegexOptions.CultureInvariant, TimeSpan.FromMilliseconds(1)); + lkeys = new(Data.Count); + foreach (string k in Data.Keys) + { + if (reg.IsMatch(k)) + lkeys.Add(k); + } + } + catch + { + return Array.Empty(); + } + } + if (lkeys.Count == 0) + return Array.Empty(); + + lkeys.Sort(); + + if (start < 0) + start = 0; + if (count < 1 || start + count > lkeys.Count) + count = lkeys.Count - start; + List result = lkeys.GetRange(start, count); + return result.ToArray(); + } + + public string[] ListKeys(int start, int count) + { + string[] keys; + lock (linksetDataLock) + { + if (Data.Count <= 0 || start >= Data.Count) + return Array.Empty(); + + keys = new string[Data.Count]; + Data.Keys.CopyTo(keys, 0); + } + Array.Sort(keys); + if (start < 0) + start = 0; + if (count < 1) + return keys[start..]; + int end = start + count; + if (end >= keys.Length) + return keys[start..]; + return keys[start..end]; + } + + /// + /// Merge the linksetData present in another Linkset into this one. + /// The current root will have the new linkset for the merged sog. + /// If a key is present in our linksetData it wins, dont overide it. + /// + /// + public void MergeOther(LinksetData otherLinksetData) + { + if (otherLinksetData is null || otherLinksetData.Data is null || otherLinksetData.Count() == 0) + return; + + lock (linksetDataLock) + { + if(m_MemoryUsed + otherLinksetData.Used() < m_MemoryLimit) + { + foreach (var kvp in otherLinksetData.Data) + { + if (Data.TryAdd(kvp.Key, kvp.Value)) + { + m_MemoryUsed += kvp.Key.Length + kvp.Value.Value.Length; + if (!string.IsNullOrEmpty(kvp.Value.Password)) + m_MemoryUsed += kvp.Value.Password.Length; + } + } + return; + } + + SortedList otherOrdered = new(otherLinksetData.Data); + foreach (var kvp in otherOrdered) + { + int mem = kvp.Key.Length + kvp.Value.Value.Length; + if (!string.IsNullOrEmpty(kvp.Value.Password)) + mem += kvp.Value.Password.Length; + if (m_MemoryUsed + mem >= m_MemoryLimit) + return; + if (Data.TryAdd(kvp.Key, kvp.Value)) + { + m_MemoryUsed += mem; + if (!string.IsNullOrEmpty(kvp.Value.Password)) + m_MemoryUsed += kvp.Value.Password.Length; + } + } + + otherLinksetData.Data = null; + otherLinksetData.m_MemoryUsed = 0; + } + } + + /// + /// ResetLinksetData - clear the list and update the accounting. + /// + public void ResetLinksetData() + { + lock (linksetDataLock) + { + if (Data.Count <= 0) + return; + + Data.Clear(); + m_MemoryUsed = 0; + } + } + + public string SerializeLinksetData() + { + lock (linksetDataLock) + { + return JsonSerializer.Serialize>(Data); + } + } + } + + public class LinksetDataEntry + { + public string Password; + public string Value; + + public LinksetDataEntry() { } + public LinksetDataEntry(string value, string password) + { + Value = value; + Password = password; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool CheckPassword(string pass) + { + // A undocumented caveat for LinksetData appears to be that even for unprotected values, + // if a pass is provided, it is still treated as protected + return string.IsNullOrEmpty(Password) || (Password == pass); + } + + public LinksetDataEntry Copy() + { + return new LinksetDataEntry + { + Password = Password, + Value = Value + }; + } + + public bool IsProtected + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return !string.IsNullOrEmpty(Password); } + } + public bool IsNotProtected + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return string.IsNullOrEmpty(Password); } + } + } +}