mirror of https://github.com/opensim/opensim.git
missing file...
This commit is contained in:
parent
2e26b8a8bb
commit
fbdb6c420f
|
@ -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<string, LinksetDataEntry> Data;
|
||||
|
||||
public LinksetData(int limit)
|
||||
{
|
||||
Data = new Dictionary<string, LinksetDataEntry>();
|
||||
|
||||
m_MemoryLimit = limit;
|
||||
m_MemoryUsed = 0;
|
||||
}
|
||||
|
||||
public LinksetData(int limit, int used)
|
||||
{
|
||||
Data = new Dictionary<string, LinksetDataEntry>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds or updates a entry to linkset data
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// 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
|
||||
/// </returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a named key from the key value store
|
||||
/// </summary>
|
||||
/// <param name="key">The key value we're removing</param>
|
||||
/// <param name="pass">The password for a protected field (or string.Empty if not protected)</param>
|
||||
/// <returns>
|
||||
/// 0 if successful.
|
||||
/// 1 if not due to the password.
|
||||
/// -1 if no such key was found
|
||||
/// </returns>
|
||||
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<string> ret;
|
||||
|
||||
lock (linksetDataLock)
|
||||
{
|
||||
if (Data.Count <= 0)
|
||||
return Array.Empty<string>();
|
||||
try
|
||||
{
|
||||
ret = new List<string>();
|
||||
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<string>();
|
||||
}
|
||||
|
||||
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<string> lkeys;
|
||||
lock (linksetDataLock)
|
||||
{
|
||||
if (Data.Count <= 0 || start >= Data.Count)
|
||||
return Array.Empty<string>();
|
||||
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<string>();
|
||||
}
|
||||
}
|
||||
if (lkeys.Count == 0)
|
||||
return Array.Empty<string>();
|
||||
|
||||
lkeys.Sort();
|
||||
|
||||
if (start < 0)
|
||||
start = 0;
|
||||
if (count < 1 || start + count > lkeys.Count)
|
||||
count = lkeys.Count - start;
|
||||
List<string> 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<string>();
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="otherLinkset"></param>
|
||||
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<string,LinksetDataEntry> 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ResetLinksetData - clear the list and update the accounting.
|
||||
/// </summary>
|
||||
public void ResetLinksetData()
|
||||
{
|
||||
lock (linksetDataLock)
|
||||
{
|
||||
if (Data.Count <= 0)
|
||||
return;
|
||||
|
||||
Data.Clear();
|
||||
m_MemoryUsed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public string SerializeLinksetData()
|
||||
{
|
||||
lock (linksetDataLock)
|
||||
{
|
||||
return JsonSerializer.Serialize<Dictionary<string, LinksetDataEntry>>(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); }
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue