mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-14 16:51:28 +00:00
[+] TEngineServer
[+] TEngineServer
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8618
|
||||
|
||||
namespace TEngine.DataStructure
|
||||
{
|
||||
/// <summary>
|
||||
/// 环形缓存(自动扩充、不会收缩缓存、所以不要用这个操作过大的IO流)
|
||||
/// 1、环大小8192,溢出的会自动增加环的大小。
|
||||
/// 2、每个块都是一个环形缓存,当溢出的时候会自动添加到下一个环中。
|
||||
/// 3、当读取完成后用过的环会放在缓存中,不会销毁掉。
|
||||
/// </summary>
|
||||
public sealed class CircularBuffer : Stream, IDisposable
|
||||
{
|
||||
private byte[] _lastBuffer;
|
||||
public const int ChunkSize = 8192; // 环形缓存块大小
|
||||
private readonly Queue<byte[]> _bufferCache = new Queue<byte[]>();
|
||||
private readonly Queue<byte[]> _bufferQueue = new Queue<byte[]>();
|
||||
public int FirstIndex { get; set; }
|
||||
public int LastIndex { get; set; }
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_bufferQueue.Count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (_bufferQueue.Count - 1) * ChunkSize + LastIndex - FirstIndex;
|
||||
}
|
||||
}
|
||||
public byte[] First
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_bufferQueue.Count == 0)
|
||||
{
|
||||
AddLast();
|
||||
}
|
||||
|
||||
return _bufferQueue.Peek();
|
||||
}
|
||||
}
|
||||
public byte[] Last
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_bufferQueue.Count == 0)
|
||||
{
|
||||
AddLast();
|
||||
}
|
||||
|
||||
return _lastBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddLast()
|
||||
{
|
||||
var buffer = _bufferCache.Count > 0 ? _bufferCache.Dequeue() : new byte[ChunkSize];
|
||||
_bufferQueue.Enqueue(buffer);
|
||||
_lastBuffer = buffer;
|
||||
}
|
||||
|
||||
public void RemoveFirst()
|
||||
{
|
||||
_bufferCache.Enqueue(_bufferQueue.Dequeue());
|
||||
}
|
||||
|
||||
public void Read(Stream stream, int count)
|
||||
{
|
||||
if (count > Length)
|
||||
{
|
||||
throw new Exception($"bufferList length < count, {Length} {count}");
|
||||
}
|
||||
|
||||
var copyCount = 0;
|
||||
while (copyCount < count)
|
||||
{
|
||||
var n = count - copyCount;
|
||||
if (ChunkSize - FirstIndex > n)
|
||||
{
|
||||
stream.Write(First, FirstIndex, n);
|
||||
FirstIndex += n;
|
||||
copyCount += n;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.Write(First, FirstIndex, ChunkSize - FirstIndex);
|
||||
copyCount += ChunkSize - FirstIndex;
|
||||
FirstIndex = 0;
|
||||
RemoveFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer.Length < offset + count)
|
||||
{
|
||||
throw new Exception($"buffer length < count, buffer length: {buffer.Length} {offset} {count}");
|
||||
}
|
||||
|
||||
var length = Length;
|
||||
if (length < count)
|
||||
{
|
||||
count = (int) length;
|
||||
}
|
||||
|
||||
var copyCount = 0;
|
||||
while (copyCount < count)
|
||||
{
|
||||
var copyLength = count - copyCount;
|
||||
|
||||
if (ChunkSize - FirstIndex > copyLength)
|
||||
{
|
||||
Array.Copy(First, FirstIndex, buffer, copyCount + offset, copyLength);
|
||||
|
||||
FirstIndex += copyLength;
|
||||
copyCount += copyLength;
|
||||
continue;
|
||||
}
|
||||
|
||||
Array.Copy(First, FirstIndex, buffer, copyCount + offset, ChunkSize - FirstIndex);
|
||||
|
||||
copyCount += ChunkSize - FirstIndex;
|
||||
FirstIndex = 0;
|
||||
|
||||
RemoveFirst();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public void Write(byte[] buffer)
|
||||
{
|
||||
Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
var copyCount = 0;
|
||||
var count = (int) (stream.Length - stream.Position);
|
||||
|
||||
while (copyCount < count)
|
||||
{
|
||||
if (LastIndex == ChunkSize)
|
||||
{
|
||||
AddLast();
|
||||
LastIndex = 0;
|
||||
}
|
||||
|
||||
var n = count - copyCount;
|
||||
|
||||
if (ChunkSize - LastIndex > n)
|
||||
{
|
||||
_ = stream.Read(Last, LastIndex, n);
|
||||
LastIndex += count - copyCount;
|
||||
copyCount += n;
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = stream.Read(Last, LastIndex, ChunkSize - LastIndex);
|
||||
copyCount += ChunkSize - LastIndex;
|
||||
LastIndex = ChunkSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
var copyCount = 0;
|
||||
|
||||
while (copyCount < count)
|
||||
{
|
||||
if (ChunkSize == LastIndex)
|
||||
{
|
||||
AddLast();
|
||||
LastIndex = 0;
|
||||
}
|
||||
|
||||
var byteLength = count - copyCount;
|
||||
|
||||
if (ChunkSize - LastIndex > byteLength)
|
||||
{
|
||||
Array.Copy(buffer, copyCount + offset, Last, LastIndex, byteLength);
|
||||
LastIndex += byteLength;
|
||||
copyCount += byteLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(buffer, copyCount + offset, _lastBuffer, LastIndex, ChunkSize - LastIndex);
|
||||
copyCount += ChunkSize - LastIndex;
|
||||
LastIndex = ChunkSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead { get; } = true;
|
||||
public override bool CanSeek { get; } = false;
|
||||
public override bool CanWrite { get; } = true;
|
||||
public override long Position { get; set; }
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public new void Dispose()
|
||||
{
|
||||
_bufferQueue.Clear();
|
||||
_lastBuffer = null;
|
||||
FirstIndex = 0;
|
||||
LastIndex = 0;
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 28188b5e2acefe14a996fb93df6b2fc6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#pragma warning disable CS8603
|
||||
|
||||
namespace TEngine.DataStructure
|
||||
{
|
||||
public class ConcurrentOneToManyListPool<TKey, TValue> : ConcurrentOneToManyList<TKey, TValue>, IDisposable where TKey : notnull
|
||||
{
|
||||
private bool _isDispose;
|
||||
|
||||
public static ConcurrentOneToManyListPool<TKey, TValue> Create()
|
||||
{
|
||||
var a = Pool<ConcurrentOneToManyListPool<TKey, TValue>>.Rent();
|
||||
a._isDispose = false;
|
||||
return a;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
Pool<ConcurrentOneToManyListPool<TKey, TValue>>.Return(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class ConcurrentOneToManyList<TKey, TValue> : ConcurrentDictionary<TKey, List<TValue>> where TKey : notnull
|
||||
{
|
||||
private readonly Queue<List<TValue>> _queue = new Queue<List<TValue>>();
|
||||
private readonly int _recyclingLimit = 120;
|
||||
|
||||
public ConcurrentOneToManyList()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public ConcurrentOneToManyList(int recyclingLimit)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
list = Fetch();
|
||||
list.Add(value);
|
||||
base[key] = list;
|
||||
return;
|
||||
}
|
||||
|
||||
list.Add(value);
|
||||
}
|
||||
|
||||
public TValue First(TKey key)
|
||||
{
|
||||
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
|
||||
}
|
||||
|
||||
public void RemoveValue(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
list.Remove(value);
|
||||
|
||||
if (list.Count == 0) RemoveKey(key);
|
||||
}
|
||||
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryRemove(key, out var list)) return;
|
||||
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
private List<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
private void Recycle(List<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f35e267f0b797464e856a9b9244b4215
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
#pragma warning disable CS8603
|
||||
|
||||
namespace TEngine.DataStructure
|
||||
{
|
||||
public class ConcurrentOneToManyQueuePool<TKey, TValue> : ConcurrentOneToManyQueue<TKey, TValue>, IDisposable
|
||||
where TKey : notnull
|
||||
{
|
||||
private bool _isDispose;
|
||||
|
||||
public static ConcurrentOneToManyQueuePool<TKey, TValue> Create()
|
||||
{
|
||||
var a = Pool<ConcurrentOneToManyQueuePool<TKey, TValue>>.Rent();
|
||||
a._isDispose = false;
|
||||
return a;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
Pool<ConcurrentOneToManyQueue<TKey, TValue>>.Return(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class ConcurrentOneToManyQueue<TKey, TValue> : ConcurrentDictionary<TKey, Queue<TValue>> where TKey : notnull
|
||||
{
|
||||
private readonly Queue<Queue<TValue>> _queue = new Queue<Queue<TValue>>();
|
||||
private readonly int _recyclingLimit;
|
||||
|
||||
/// <summary>
|
||||
/// 设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public ConcurrentOneToManyQueue(int recyclingLimit = 0)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
public void Enqueue(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
list = Fetch();
|
||||
list.Enqueue(value);
|
||||
TryAdd(key, list);
|
||||
return;
|
||||
}
|
||||
|
||||
list.Enqueue(value);
|
||||
}
|
||||
|
||||
public TValue Dequeue(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list) || list.Count == 0) return default;
|
||||
|
||||
var value = list.Dequeue();
|
||||
|
||||
if (list.Count == 0) RemoveKey(key);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool TryDequeue(TKey key, out TValue value)
|
||||
{
|
||||
value = Dequeue(key);
|
||||
|
||||
return value != null;
|
||||
}
|
||||
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
TryRemove(key, out _);
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
private Queue<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new Queue<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
private void Recycle(Queue<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 162f7b8459ead1940bfaef049f6d8e2c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TEngine.DataStructure
|
||||
{
|
||||
public sealed class EntityList<T> : List<T>, IDisposable where T : IDisposable
|
||||
{
|
||||
private bool _isDispose;
|
||||
|
||||
public static EntityList<T> Create()
|
||||
{
|
||||
var list = Pool<EntityList<T>>.Rent();
|
||||
list._isDispose = false;
|
||||
return list;
|
||||
}
|
||||
|
||||
public new void Clear()
|
||||
{
|
||||
for (var i = 0; i < this.Count; i++)
|
||||
{
|
||||
this[i].Dispose();
|
||||
}
|
||||
|
||||
base.Clear();
|
||||
}
|
||||
|
||||
public void ClearNotDispose()
|
||||
{
|
||||
base.Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
Pool<EntityList<T>>.Return(this);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 763e9615aee0ac1428f141df39057bf7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TEngine.DataStructure
|
||||
{
|
||||
public sealed class HashSetPool<T> : HashSet<T>, IDisposable
|
||||
{
|
||||
private bool _isDispose;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
Pool<HashSetPool<T>>.Return(this);
|
||||
}
|
||||
|
||||
public static HashSetPool<T> Create()
|
||||
{
|
||||
var list = Pool<HashSetPool<T>>.Rent();
|
||||
list._isDispose = false;
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class HashSetBasePool<T> : IDisposable
|
||||
{
|
||||
public HashSet<T> Set = new HashSet<T>();
|
||||
|
||||
public static HashSetBasePool<T> Create()
|
||||
{
|
||||
return Pool<HashSetBasePool<T>>.Rent();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Set.Clear();
|
||||
Pool<HashSetBasePool<T>>.Return(this);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f2f1a1f1a3f5f246a40be1e7bff871a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TEngine.DataStructure
|
||||
{
|
||||
public sealed class ListPool<T> : List<T>, IDisposable
|
||||
{
|
||||
private bool _isDispose;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
Pool<ListPool<T>>.Return(this);
|
||||
}
|
||||
|
||||
public static ListPool<T> Create(params T[] args)
|
||||
{
|
||||
var list = Pool<ListPool<T>>.Rent();
|
||||
list._isDispose = false;
|
||||
if (args != null) list.AddRange(args);
|
||||
return list;
|
||||
}
|
||||
|
||||
public static ListPool<T> Create(List<T> args)
|
||||
{
|
||||
var list = Pool<ListPool<T>>.Rent();
|
||||
list._isDispose = false;
|
||||
if (args != null) list.AddRange(args);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3014ce89f46d33742acca54a28995242
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#pragma warning disable CS8600
|
||||
|
||||
namespace TEngine.DataStructure
|
||||
{
|
||||
public class OneToManyHashSetPool<TKey, TValue> : OneToManyHashSet<TKey, TValue>, IDisposable where TKey : notnull
|
||||
{
|
||||
private bool _isDispose;
|
||||
|
||||
public static OneToManyHashSetPool<TKey, TValue> Create()
|
||||
{
|
||||
var a = Pool<OneToManyHashSetPool<TKey, TValue>>.Rent();
|
||||
a._isDispose = false;
|
||||
return a;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
Pool<OneToManyHashSetPool<TKey, TValue>>.Return(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class OneToManyHashSet<TKey, TValue> : Dictionary<TKey, HashSet<TValue>> where TKey : notnull
|
||||
{
|
||||
private readonly Queue<HashSet<TValue>> _queue = new Queue<HashSet<TValue>>();
|
||||
private readonly int _recyclingLimit = 120;
|
||||
private static HashSet<TValue> _empty = new HashSet<TValue>();
|
||||
|
||||
public OneToManyHashSet()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public OneToManyHashSet(int recyclingLimit)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
list = Fetch();
|
||||
list.Add(value);
|
||||
Add(key, list);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
list.Add(value);
|
||||
}
|
||||
|
||||
public void RemoveValue(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
list.Remove(value);
|
||||
|
||||
if (list.Count == 0) RemoveKey(key);
|
||||
}
|
||||
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
Remove(key);
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
public HashSet<TValue> GetValue(TKey key)
|
||||
{
|
||||
if (TryGetValue(key, out HashSet<TValue> value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return _empty;
|
||||
}
|
||||
|
||||
private HashSet<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new HashSet<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
private void Recycle(HashSet<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 393302e735c4b2f4885c21dd4235b64a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#pragma warning disable CS8600
|
||||
#pragma warning disable CS8603
|
||||
|
||||
namespace TEngine.DataStructure
|
||||
{
|
||||
public class OneToManyListPool<TKey, TValue> : OneToManyList<TKey, TValue>, IDisposable where TKey : notnull
|
||||
{
|
||||
private bool _isDispose;
|
||||
|
||||
public static OneToManyListPool<TKey, TValue> Create()
|
||||
{
|
||||
var list = Pool<OneToManyListPool<TKey, TValue>>.Rent();
|
||||
list._isDispose = false;
|
||||
return list;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
Pool<OneToManyListPool<TKey, TValue>>.Return(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class OneToManyList<TKey, TValue> : Dictionary<TKey, List<TValue>> where TKey : notnull
|
||||
{
|
||||
private readonly Queue<List<TValue>> _queue = new Queue<List<TValue>>();
|
||||
private readonly int _recyclingLimit = 120;
|
||||
private static List<TValue> _empty = new List<TValue>();
|
||||
|
||||
public OneToManyList()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public OneToManyList(int recyclingLimit)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
list = Fetch();
|
||||
list.Add(value);
|
||||
Add(key, list);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
list.Add(value);
|
||||
}
|
||||
|
||||
public TValue First(TKey key)
|
||||
{
|
||||
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
|
||||
}
|
||||
|
||||
public bool RemoveValue(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var isRemove = list.Remove(value);
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
isRemove = RemoveByKey(key);
|
||||
}
|
||||
|
||||
return isRemove;
|
||||
}
|
||||
|
||||
public bool RemoveByKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Remove(key);
|
||||
Recycle(list);
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<TValue> GetValues(TKey key)
|
||||
{
|
||||
if (TryGetValue(key, out List<TValue> list))
|
||||
{
|
||||
return list;
|
||||
}
|
||||
|
||||
return _empty;
|
||||
}
|
||||
|
||||
public new void Clear()
|
||||
{
|
||||
foreach (var keyValuePair in this) Recycle(keyValuePair.Value);
|
||||
|
||||
base.Clear();
|
||||
}
|
||||
|
||||
private List<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
private void Recycle(List<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a6fdf7c8423c0e41804022df95a399d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#pragma warning disable CS8603
|
||||
|
||||
namespace TEngine.DataStructure
|
||||
{
|
||||
public class OneToManyQueuePool<TKey, TValue> : OneToManyQueue<TKey, TValue>, IDisposable where TKey : notnull
|
||||
{
|
||||
private bool _isDispose;
|
||||
|
||||
public static OneToManyQueuePool<TKey, TValue> Create()
|
||||
{
|
||||
var a = Pool<OneToManyQueuePool<TKey, TValue>>.Rent();
|
||||
a._isDispose = false;
|
||||
return a;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
Pool<OneToManyQueuePool<TKey, TValue>>.Return(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class OneToManyQueue<TKey, TValue> : Dictionary<TKey, Queue<TValue>> where TKey : notnull
|
||||
{
|
||||
private readonly Queue<Queue<TValue>> _queue = new Queue<Queue<TValue>>();
|
||||
private readonly int _recyclingLimit;
|
||||
|
||||
/// <summary>
|
||||
/// 设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public OneToManyQueue(int recyclingLimit = 0)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
public void Enqueue(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
list = Fetch();
|
||||
list.Enqueue(value);
|
||||
Add(key, list);
|
||||
return;
|
||||
}
|
||||
|
||||
list.Enqueue(value);
|
||||
}
|
||||
|
||||
public TValue Dequeue(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list) || list.Count == 0)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var value = list.Dequeue();
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
RemoveKey(key);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool TryDequeue(TKey key, out TValue value)
|
||||
{
|
||||
value = Dequeue(key);
|
||||
|
||||
return value != null;
|
||||
}
|
||||
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
Remove(key);
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
private Queue<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new Queue<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
private void Recycle(Queue<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35d6a9fde65a99143b0abf6d9569c462
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,537 @@
|
||||
// #if UNITY_5_3_OR_NEWER
|
||||
// using System;
|
||||
// using System.Collections;
|
||||
// using System.Collections.Generic;
|
||||
// using System.Diagnostics;
|
||||
// using System.Diagnostics.CodeAnalysis;
|
||||
// using System.Linq;
|
||||
// using System.Runtime.CompilerServices;
|
||||
// #pragma warning disable CS8600
|
||||
//
|
||||
// namespace System.Collections.Generic
|
||||
// {
|
||||
// public class PriorityQueue<TElement, TPriority>
|
||||
// {
|
||||
// private const int DefaultCapacity = 4;
|
||||
//
|
||||
// private readonly IComparer<TPriority> _priorityComparer;
|
||||
//
|
||||
// private HeapEntry[] _heap;
|
||||
// private int _count;
|
||||
// private int _version;
|
||||
//
|
||||
// private UnorderedItemsCollection? _unorderedItemsCollection;
|
||||
//
|
||||
// #region Constructors
|
||||
// public PriorityQueue() : this(0, null)
|
||||
// {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// // public PriorityQueue(int initialCapacity) : this(initialCapacity, null)
|
||||
// // {
|
||||
// //
|
||||
// // }
|
||||
//
|
||||
// // public PriorityQueue(IComparer<TPriority>? comparer) : this(0, comparer)
|
||||
// // {
|
||||
// //
|
||||
// // }
|
||||
//
|
||||
// // public PriorityQueue(int initialCapacity, IComparer<TPriority>? comparer)
|
||||
// // {
|
||||
// // if (initialCapacity < 0)
|
||||
// // {
|
||||
// // throw new ArgumentOutOfRangeException(nameof(initialCapacity));
|
||||
// // }
|
||||
// //
|
||||
// // if (initialCapacity == 0)
|
||||
// // {
|
||||
// // _heap = Array.Empty<HeapEntry>();
|
||||
// // }
|
||||
// // else
|
||||
// // {
|
||||
// // _heap = new HeapEntry[initialCapacity];
|
||||
// // }
|
||||
// //
|
||||
// // _priorityComparer = comparer ?? Comparer<TPriority>.Default;
|
||||
// // }
|
||||
//
|
||||
// public PriorityQueue(IEnumerable<(TElement Element, TPriority Priority)> values) : this(values, null)
|
||||
// {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// public PriorityQueue(IEnumerable<(TElement Element, TPriority Priority)> values, IComparer<TPriority>? comparer)
|
||||
// {
|
||||
// _priorityComparer = comparer ?? Comparer<TPriority>.Default;
|
||||
// _heap = Array.Empty<HeapEntry>();
|
||||
// _count = 0;
|
||||
//
|
||||
// AppendRaw(values);
|
||||
// Heapify();
|
||||
// }
|
||||
// #endregion
|
||||
//
|
||||
// public int Count => _count;
|
||||
// public IComparer<TPriority> Comparer => _priorityComparer;
|
||||
//
|
||||
// public void Enqueue(TElement element, TPriority priority)
|
||||
// {
|
||||
// _version++;
|
||||
// if (_count == _heap.Length)
|
||||
// {
|
||||
// Resize(ref _heap);
|
||||
// }
|
||||
//
|
||||
// SiftUp(index: _count++, in element, in priority);
|
||||
// }
|
||||
//
|
||||
// public void EnqueueRange(IEnumerable<(TElement Element, TPriority Priority)> values)
|
||||
// {
|
||||
// _version++;
|
||||
// if (_count == 0)
|
||||
// {
|
||||
// AppendRaw(values);
|
||||
// Heapify();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// foreach ((TElement element, TPriority priority) in values)
|
||||
// {
|
||||
// if (_count == _heap.Length)
|
||||
// {
|
||||
// Resize(ref _heap);
|
||||
// }
|
||||
//
|
||||
// SiftUp(index: _count++, in element, in priority);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // TODO optimize
|
||||
// public void EnqueueRange(IEnumerable<TElement> elements, TPriority priority) => EnqueueRange(elements.Select(e => (e, priority)));
|
||||
//
|
||||
// public TElement Peek()
|
||||
// {
|
||||
// if (_count == 0)
|
||||
// {
|
||||
// throw new InvalidOperationException();
|
||||
// }
|
||||
//
|
||||
// return _heap[0].Element;
|
||||
// }
|
||||
//
|
||||
// public bool TryPeek([MaybeNullWhen(false)] out TElement element, [MaybeNullWhen(false)] out TPriority priority)
|
||||
// {
|
||||
// if (_count == 0)
|
||||
// {
|
||||
// element = default;
|
||||
// priority = default;
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// (element, priority) = _heap[0];
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// public TElement Dequeue()
|
||||
// {
|
||||
// if (_count == 0)
|
||||
// {
|
||||
// throw new InvalidOperationException();
|
||||
// }
|
||||
//
|
||||
// _version++;
|
||||
// RemoveIndex(index: 0, out TElement result, out _);
|
||||
// return result;
|
||||
// }
|
||||
//
|
||||
// public bool TryDequeue([MaybeNullWhen(false)] out TElement element, [MaybeNullWhen(false)] out TPriority priority)
|
||||
// {
|
||||
// if (_count == 0)
|
||||
// {
|
||||
// element = default;
|
||||
// priority = default;
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// _version++;
|
||||
// RemoveIndex(index: 0, out element, out priority);
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// public TElement EnqueueDequeue(TElement element, TPriority priority)
|
||||
// {
|
||||
// if (_count == 0)
|
||||
// {
|
||||
// return element;
|
||||
// }
|
||||
//
|
||||
// ref HeapEntry minEntry = ref _heap[0];
|
||||
// if (_priorityComparer.Compare(priority, minEntry.Priority) <= 0)
|
||||
// {
|
||||
// return element;
|
||||
// }
|
||||
//
|
||||
// _version++;
|
||||
// TElement minElement = minEntry.Element;
|
||||
// #if SIFTDOWN_EMPTY_NODES
|
||||
// SiftDownHeapPropertyRequired(index: 0, in element, in priority);
|
||||
// #else
|
||||
// SiftDown(index: 0, in element, in priority);
|
||||
// #endif
|
||||
// return minElement;
|
||||
// }
|
||||
//
|
||||
// public void Clear()
|
||||
// {
|
||||
// _version++;
|
||||
// if (_count > 0)
|
||||
// {
|
||||
// //if (RuntimeHelpers.IsReferenceOrContainsReferences<HeapEntry>())
|
||||
// {
|
||||
// Array.Clear(_heap, 0, _count);
|
||||
// }
|
||||
//
|
||||
// _count = 0;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public void TrimExcess()
|
||||
// {
|
||||
// int count = _count;
|
||||
// int threshold = (int)(((double)_heap.Length) * 0.9);
|
||||
// if (count < threshold)
|
||||
// {
|
||||
// Array.Resize(ref _heap, count);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public void EnsureCapacity(int capacity)
|
||||
// {
|
||||
// if (capacity < 0)
|
||||
// {
|
||||
// throw new ArgumentOutOfRangeException();
|
||||
// }
|
||||
//
|
||||
// if (capacity > _heap.Length)
|
||||
// {
|
||||
// Array.Resize(ref _heap, capacity);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public UnorderedItemsCollection UnorderedItems => _unorderedItemsCollection ??= new UnorderedItemsCollection(this);
|
||||
//
|
||||
// public class UnorderedItemsCollection : IReadOnlyCollection<(TElement Element, TPriority Priority)>, ICollection
|
||||
// {
|
||||
// private readonly PriorityQueue<TElement, TPriority> _priorityQueue;
|
||||
//
|
||||
// internal UnorderedItemsCollection(PriorityQueue<TElement, TPriority> priorityQueue)
|
||||
// {
|
||||
// _priorityQueue = priorityQueue;
|
||||
// }
|
||||
//
|
||||
// public int Count => _priorityQueue.Count;
|
||||
// public bool IsSynchronized => false;
|
||||
// public object SyncRoot => _priorityQueue;
|
||||
//
|
||||
// public Enumerator GetEnumerator() => new Enumerator(_priorityQueue);
|
||||
// IEnumerator<(TElement Element, TPriority Priority)> IEnumerable<(TElement Element, TPriority Priority)>.GetEnumerator() => new Enumerator(_priorityQueue);
|
||||
// IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_priorityQueue);
|
||||
//
|
||||
// bool ICollection.IsSynchronized => false;
|
||||
// object ICollection.SyncRoot => this;
|
||||
// void ICollection.CopyTo(Array array, int index)
|
||||
// {
|
||||
// if (array == null)
|
||||
// throw new ArgumentNullException(nameof(array));
|
||||
// if (array.Rank != 1)
|
||||
// throw new ArgumentException("SR.Arg_RankMultiDimNotSupported", nameof(array));
|
||||
// if (index < 0)
|
||||
// throw new ArgumentOutOfRangeException(nameof(index), "SR.ArgumentOutOfRange_Index");
|
||||
//
|
||||
// int arrayLen = array.Length;
|
||||
// if (arrayLen - index < _priorityQueue._count)
|
||||
// throw new ArgumentException("SR.Argument_InvalidOffLen");
|
||||
//
|
||||
// int numToCopy = _priorityQueue._count;
|
||||
// HeapEntry[] heap = _priorityQueue._heap;
|
||||
//
|
||||
// for (int i = 0; i < numToCopy; i++)
|
||||
// {
|
||||
// ref HeapEntry entry = ref heap[i];
|
||||
// array.SetValue((entry.Element, entry.Priority), index + i);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public struct Enumerator : IEnumerator<(TElement Element, TPriority Priority)>, IEnumerator
|
||||
// {
|
||||
// private readonly PriorityQueue<TElement, TPriority> _queue;
|
||||
// private readonly int _version;
|
||||
// private int _index;
|
||||
// private (TElement Element, TPriority Priority) _current;
|
||||
//
|
||||
// internal Enumerator(PriorityQueue<TElement, TPriority> queue)
|
||||
// {
|
||||
// _version = queue._version;
|
||||
// _queue = queue;
|
||||
// _index = 0;
|
||||
// _current = default;
|
||||
// }
|
||||
//
|
||||
// public bool MoveNext()
|
||||
// {
|
||||
// PriorityQueue<TElement, TPriority> queue = _queue;
|
||||
//
|
||||
// if (queue._version == _version && _index < queue._count)
|
||||
// {
|
||||
// ref HeapEntry entry = ref queue._heap[_index];
|
||||
// _current = (entry.Element, entry.Priority);
|
||||
// _index++;
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// if (queue._version != _version)
|
||||
// {
|
||||
// throw new InvalidOperationException("collection was modified");
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// public (TElement Element, TPriority Priority) Current => _current;
|
||||
// object IEnumerator.Current => _current;
|
||||
//
|
||||
// public void Reset()
|
||||
// {
|
||||
// if (_queue._version != _version)
|
||||
// {
|
||||
// throw new InvalidOperationException("collection was modified");
|
||||
// }
|
||||
//
|
||||
// _index = 0;
|
||||
// _current = default;
|
||||
// }
|
||||
//
|
||||
// public void Dispose()
|
||||
// {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #region Private Methods
|
||||
// private void Heapify()
|
||||
// {
|
||||
// HeapEntry[] heap = _heap;
|
||||
//
|
||||
// for (int i = (_count - 1) >> 2; i >= 0; i--)
|
||||
// {
|
||||
// HeapEntry entry = heap[i]; // ensure struct is copied before sifting
|
||||
// SiftDown(i, in entry.Element, in entry.Priority);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void AppendRaw(IEnumerable<(TElement Element, TPriority Priority)> values)
|
||||
// {
|
||||
// // TODO: specialize on ICollection types
|
||||
// var heap = _heap;
|
||||
// int count = _count;
|
||||
//
|
||||
// foreach ((TElement element, TPriority priority) in values)
|
||||
// {
|
||||
// if (count == heap.Length)
|
||||
// {
|
||||
// Resize(ref heap);
|
||||
// }
|
||||
//
|
||||
// ref HeapEntry entry = ref heap[count];
|
||||
// entry.Element = element;
|
||||
// entry.Priority = priority;
|
||||
// count++;
|
||||
// }
|
||||
//
|
||||
// _heap = heap;
|
||||
// _count = count;
|
||||
// }
|
||||
//
|
||||
// private void RemoveIndex(int index, out TElement element, out TPriority priority)
|
||||
// {
|
||||
// Debug.Assert(index < _count);
|
||||
//
|
||||
// (element, priority) = _heap[index];
|
||||
//
|
||||
// int lastElementPos = --_count;
|
||||
// ref HeapEntry lastElement = ref _heap[lastElementPos];
|
||||
//
|
||||
// if (lastElementPos > 0)
|
||||
// {
|
||||
// #if SIFTDOWN_EMPTY_NODES
|
||||
// SiftDownHeapPropertyRequired(index, in lastElement.Element, in lastElement.Priority);
|
||||
// #else
|
||||
// SiftDown(index, in lastElement.Element, in lastElement.Priority);
|
||||
// #endif
|
||||
// }
|
||||
//
|
||||
// //if (RuntimeHelpers.IsReferenceOrContainsReferences<HeapEntry>())
|
||||
// {
|
||||
// lastElement = default;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void SiftUp(int index, in TElement element, in TPriority priority)
|
||||
// {
|
||||
// while (index > 0)
|
||||
// {
|
||||
// int parentIndex = (index - 1) >> 2;
|
||||
// ref HeapEntry parent = ref _heap[parentIndex];
|
||||
//
|
||||
// if (_priorityComparer.Compare(parent.Priority, priority) <= 0)
|
||||
// {
|
||||
// // parentPriority <= priority, heap property is satisfed
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// _heap[index] = parent;
|
||||
// index = parentIndex;
|
||||
// }
|
||||
//
|
||||
// ref HeapEntry entry = ref _heap[index];
|
||||
// entry.Element = element;
|
||||
// entry.Priority = priority;
|
||||
// }
|
||||
//
|
||||
// private void SiftDown(int index, in TElement element, in TPriority priority)
|
||||
// {
|
||||
// int minChildIndex;
|
||||
// int count = _count;
|
||||
// HeapEntry[] heap = _heap;
|
||||
//
|
||||
// while ((minChildIndex = (index << 2) + 1) < count)
|
||||
// {
|
||||
// // find the child with the minimal priority
|
||||
// ref HeapEntry minChild = ref heap[minChildIndex];
|
||||
// int childUpperBound = Math.Min(count, minChildIndex + 4);
|
||||
//
|
||||
// for (int nextChildIndex = minChildIndex + 1; nextChildIndex < childUpperBound; nextChildIndex++)
|
||||
// {
|
||||
// ref HeapEntry nextChild = ref heap[nextChildIndex];
|
||||
// if (_priorityComparer.Compare(nextChild.Priority, minChild.Priority) < 0)
|
||||
// {
|
||||
// minChildIndex = nextChildIndex;
|
||||
// minChild = ref nextChild;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // compare with inserted priority
|
||||
// if (_priorityComparer.Compare(priority, minChild.Priority) <= 0)
|
||||
// {
|
||||
// // priority <= minChild, heap property is satisfied
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// heap[index] = minChild;
|
||||
// index = minChildIndex;
|
||||
// }
|
||||
//
|
||||
// ref HeapEntry entry = ref heap[index];
|
||||
// entry.Element = element;
|
||||
// entry.Priority = priority;
|
||||
// }
|
||||
//
|
||||
// #if SIFTDOWN_EMPTY_NODES
|
||||
// private void SiftDownHeapPropertyRequired(int index, in TElement element, in TPriority priority)
|
||||
// {
|
||||
// int emptyNodeIndex = SiftDownEmptyNode(index);
|
||||
// SiftUp(emptyNodeIndex, in element, in priority);
|
||||
// }
|
||||
//
|
||||
// private int SiftDownEmptyNode(int emptyNodeIndex)
|
||||
// {
|
||||
// int count = _count;
|
||||
// int minChildIndex;
|
||||
// HeapEntry[] heap = _heap;
|
||||
//
|
||||
// while ((minChildIndex = (emptyNodeIndex << 2) + 1) < count)
|
||||
// {
|
||||
// // find the child with the minimal priority
|
||||
// ref HeapEntry minChild = ref heap[minChildIndex];
|
||||
// int childUpperBound = Math.Min(count, minChildIndex + 4);
|
||||
//
|
||||
// for (int nextChildIndex = minChildIndex + 1; nextChildIndex < childUpperBound; nextChildIndex++)
|
||||
// {
|
||||
// ref HeapEntry nextChild = ref heap[nextChildIndex];
|
||||
// if (_priorityComparer.Compare(nextChild.Priority, minChild.Priority) < 0)
|
||||
// {
|
||||
// minChildIndex = nextChildIndex;
|
||||
// minChild = ref nextChild;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// heap[emptyNodeIndex] = minChild;
|
||||
// emptyNodeIndex = minChildIndex;
|
||||
// }
|
||||
//
|
||||
// return emptyNodeIndex;
|
||||
// }
|
||||
// #endif
|
||||
//
|
||||
// private void Resize(ref HeapEntry[] heap)
|
||||
// {
|
||||
// int newSize = heap.Length == 0 ? DefaultCapacity : 2 * heap.Length;
|
||||
// Array.Resize(ref heap, newSize);
|
||||
// }
|
||||
//
|
||||
// private struct HeapEntry
|
||||
// {
|
||||
// public TElement Element;
|
||||
// public TPriority Priority;
|
||||
//
|
||||
// public void Deconstruct(out TElement element, out TPriority priority)
|
||||
// {
|
||||
// element = Element;
|
||||
// priority = Priority;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #if DEBUG
|
||||
// public void ValidateInternalState()
|
||||
// {
|
||||
// if (_heap.Length < _count)
|
||||
// {
|
||||
// throw new Exception("invalid elements array length");
|
||||
// }
|
||||
//
|
||||
// foreach ((var element, var idx) in _heap.Select((x, i) => (x.Element, i)).Skip(_count))
|
||||
// {
|
||||
// if (!IsDefault(element))
|
||||
// {
|
||||
// throw new Exception($"Non-zero element '{element}' at index {idx}.");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// foreach ((var priority, var idx) in _heap.Select((x, i) => (x.Priority, i)).Skip(_count))
|
||||
// {
|
||||
// if (!IsDefault(priority))
|
||||
// {
|
||||
// throw new Exception($"Non-zero priority '{priority}' at index {idx}.");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// static bool IsDefault<T>(T value)
|
||||
// {
|
||||
// T defaultVal = default;
|
||||
//
|
||||
// if (defaultVal is null)
|
||||
// {
|
||||
// return value is null;
|
||||
// }
|
||||
//
|
||||
// return value!.Equals(defaultVal);
|
||||
// }
|
||||
// }
|
||||
// #endif
|
||||
// #endregion
|
||||
// }
|
||||
// }
|
||||
// #endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e7e9292b9c27384d9aed58ea3d5098d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TEngine.DataStructure
|
||||
{
|
||||
public sealed class ReuseList<T> : List<T>, IDisposable
|
||||
{
|
||||
private bool _isDispose;
|
||||
|
||||
public static ReuseList<T> Create()
|
||||
{
|
||||
var list = Pool<ReuseList<T>>.Rent();
|
||||
list._isDispose = false;
|
||||
return list;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
Pool<ReuseList<T>>.Return(this);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fe34244ec94ec0240a92d11c66f9d94e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#pragma warning disable CS8603
|
||||
|
||||
namespace TEngine.DataStructure
|
||||
{
|
||||
public class SortedConcurrentOneToManyListPool<TKey, TValue> : SortedConcurrentOneToManyList<TKey, TValue>,
|
||||
IDisposable where TKey : notnull
|
||||
{
|
||||
private bool _isDispose;
|
||||
|
||||
public static SortedConcurrentOneToManyListPool<TKey, TValue> Create()
|
||||
{
|
||||
var a = Pool<SortedConcurrentOneToManyListPool<TKey, TValue>>.Rent();
|
||||
a._isDispose = false;
|
||||
return a;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
Pool<SortedConcurrentOneToManyListPool<TKey, TValue>>.Return(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class SortedConcurrentOneToManyList<TKey, TValue> : SortedDictionary<TKey, List<TValue>> where TKey : notnull
|
||||
{
|
||||
private readonly object _lockObject = new object();
|
||||
private readonly Queue<List<TValue>> _queue = new Queue<List<TValue>>();
|
||||
private readonly int _recyclingLimit;
|
||||
|
||||
public SortedConcurrentOneToManyList()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public SortedConcurrentOneToManyList(int recyclingLimit = 0)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
list = Fetch();
|
||||
list.Add(value);
|
||||
base[key] = list;
|
||||
return;
|
||||
}
|
||||
|
||||
list.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
public TValue First(TKey key)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveValue(TKey key, TValue value)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
list.Remove(value);
|
||||
|
||||
if (list.Count == 0) RemoveKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
Remove(key);
|
||||
|
||||
Recycle(list);
|
||||
}
|
||||
}
|
||||
|
||||
private List<TValue> Fetch()
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
private void Recycle(List<TValue> list)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
}
|
||||
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24aa8bc8ac4d8584cb9ceb23a51c78fa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TEngine.DataStructure
|
||||
{
|
||||
public class SortedOneToManyHashSetPool<TKey, TValue> : SortedOneToManyHashSet<TKey, TValue>, IDisposable
|
||||
where TKey : notnull
|
||||
{
|
||||
private bool _isDispose;
|
||||
|
||||
public static SortedOneToManyHashSetPool<TKey, TValue> Create()
|
||||
{
|
||||
var a = Pool<SortedOneToManyHashSetPool<TKey, TValue>>.Rent();
|
||||
a._isDispose = false;
|
||||
return a;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
Pool<SortedOneToManyHashSetPool<TKey, TValue>>.Return(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class SortedOneToManyHashSet<TKey, TValue> : SortedDictionary<TKey, HashSet<TValue>> where TKey : notnull
|
||||
{
|
||||
private readonly Queue<HashSet<TValue>> _queue = new Queue<HashSet<TValue>>();
|
||||
private readonly int _recyclingLimit = 120;
|
||||
|
||||
public SortedOneToManyHashSet()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public SortedOneToManyHashSet(int recyclingLimit)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
list = Fetch();
|
||||
list.Add(value);
|
||||
Add(key, list);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
list.Add(value);
|
||||
}
|
||||
|
||||
public void RemoveValue(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
list.Remove(value);
|
||||
|
||||
if (list.Count == 0) RemoveKey(key);
|
||||
}
|
||||
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
Remove(key);
|
||||
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
private HashSet<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new HashSet<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
private void Recycle(HashSet<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ddf73601eadf11349b77b78a4d91f35a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#pragma warning disable CS8603
|
||||
|
||||
namespace TEngine.DataStructure
|
||||
{
|
||||
public class SortedOneToManyListPool<TKey, TValue> : SortedOneToManyList<TKey, TValue>, IDisposable
|
||||
where TKey : notnull
|
||||
{
|
||||
private bool _isDispose;
|
||||
|
||||
public static SortedOneToManyListPool<TKey, TValue> Create()
|
||||
{
|
||||
var a = Pool<SortedOneToManyListPool<TKey, TValue>>.Rent();
|
||||
a._isDispose = false;
|
||||
return a;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
Pool<SortedOneToManyListPool<TKey, TValue>>.Return(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class SortedOneToManyList<TKey, TValue> : SortedDictionary<TKey, List<TValue>> where TKey : notnull
|
||||
{
|
||||
private readonly Queue<List<TValue>> _queue = new Queue<List<TValue>>();
|
||||
private readonly int _recyclingLimit;
|
||||
|
||||
public SortedOneToManyList()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public SortedOneToManyList(int recyclingLimit = 0)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
list = Fetch();
|
||||
list.Add(value);
|
||||
base[key] = list;
|
||||
return;
|
||||
}
|
||||
|
||||
list.Add(value);
|
||||
}
|
||||
|
||||
public TValue First(TKey key)
|
||||
{
|
||||
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
|
||||
}
|
||||
|
||||
public void RemoveValue(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
list.Remove(value);
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
RemoveKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Remove(key);
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
private List<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
private void Recycle(List<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db62c1998de4d5048bde5d5e09d6f284
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user