提交修改
This commit is contained in:
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Assembly.meta
vendored
Normal file
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Assembly.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5b24e6eec64b4702b871053139f8add
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
91
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Assembly/AssemblyLifecycle.cs
vendored
Normal file
91
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Assembly/AssemblyLifecycle.cs
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Async;
|
||||
|
||||
namespace Fantasy.Assembly
|
||||
{
|
||||
/// <summary>
|
||||
/// 程序集生命周期管理类
|
||||
/// 管理所有注册的程序集生命周期回调,在程序集加载、卸载时触发相应的回调方法
|
||||
/// </summary>
|
||||
public static class AssemblyLifecycle
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
/// <summary>
|
||||
/// 程序集生命周期回调集合(WebGL 单线程版本)
|
||||
/// </summary>
|
||||
private static readonly Dictionary<IAssemblyLifecycle, byte> AssemblyLifecycles = new Dictionary<IAssemblyLifecycle, byte>();
|
||||
#else
|
||||
/// <summary>
|
||||
/// 程序集生命周期回调集合(线程安全版本)
|
||||
/// 使用 ConcurrentDictionary 当作 Set 使用,Value 无实际意义
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<IAssemblyLifecycle, byte> AssemblyLifecycles = new ConcurrentDictionary<IAssemblyLifecycle, byte>();
|
||||
#endif
|
||||
/// <summary>
|
||||
/// 触发程序集加载事件
|
||||
/// 遍历所有已注册的生命周期回调,调用其 OnLoad 方法
|
||||
/// </summary>
|
||||
/// <param name="assemblyManifest">程序集清单对象</param>
|
||||
/// <returns>异步任务</returns>
|
||||
internal static async FTask OnLoad(AssemblyManifest assemblyManifest)
|
||||
{
|
||||
foreach (var (assemblyLifecycle, _) in AssemblyLifecycles)
|
||||
{
|
||||
await assemblyLifecycle.OnLoad(assemblyManifest);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发程序集卸载事件
|
||||
/// 遍历所有已注册的生命周期回调,调用其 OnUnload 方法,并清理程序集清单
|
||||
/// </summary>
|
||||
/// <param name="assemblyManifest">程序集清单对象</param>
|
||||
/// <returns>异步任务</returns>
|
||||
internal static async FTask OnUnLoad(AssemblyManifest assemblyManifest)
|
||||
{
|
||||
foreach (var (assemblyLifecycle, _) in AssemblyLifecycles)
|
||||
{
|
||||
await assemblyLifecycle.OnUnload(assemblyManifest);
|
||||
}
|
||||
assemblyManifest.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加程序集生命周期回调
|
||||
/// 添加后会立即对所有已加载的程序集触发 Load 回调
|
||||
/// </summary>
|
||||
/// <param name="assemblyLifecycle">实现 IAssemblyLifecycle 接口的生命周期回调对象</param>
|
||||
internal static async FTask Add(IAssemblyLifecycle assemblyLifecycle)
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
AssemblyLifecycles.Add(assemblyLifecycle, 0);
|
||||
#else
|
||||
AssemblyLifecycles.TryAdd(assemblyLifecycle, 0);
|
||||
#endif
|
||||
foreach (var (_, assemblyManifest) in AssemblyManifest.Manifests)
|
||||
{
|
||||
await assemblyLifecycle.OnLoad(assemblyManifest);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除程序集生命周期回调
|
||||
/// 移除后该回调将不再接收程序集的加载、卸载、重载事件
|
||||
/// </summary>
|
||||
/// <param name="assemblyLifecycle">要移除的生命周期回调对象</param>
|
||||
internal static void Remove(IAssemblyLifecycle assemblyLifecycle)
|
||||
{
|
||||
AssemblyLifecycles.Remove(assemblyLifecycle, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放所有程序集生命周期回调
|
||||
/// 清空所有已注册的生命周期回调集合
|
||||
/// </summary>
|
||||
public static void Dispose()
|
||||
{
|
||||
AssemblyLifecycles.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Assembly/AssemblyLifecycle.cs.meta
vendored
Normal file
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Assembly/AssemblyLifecycle.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc34b85d59dac6a4dad420cfa302e4c7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
256
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Assembly/AssemblyManifest.cs
vendored
Normal file
256
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Assembly/AssemblyManifest.cs
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Async;
|
||||
using Fantasy.DataStructure.Collection;
|
||||
using Fantasy.Entitas;
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
|
||||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
#pragma warning disable CS8603 // Possible null reference return.
|
||||
|
||||
namespace Fantasy.Assembly
|
||||
{
|
||||
/// <summary>
|
||||
/// 程序集清单类
|
||||
/// 封装程序集的元数据和各种系统注册器,用于统一管理程序集的生命周期和系统注册
|
||||
/// </summary>
|
||||
public sealed class AssemblyManifest
|
||||
{
|
||||
/// <summary>
|
||||
/// 程序集唯一标识符
|
||||
/// 通过程序集名称的哈希值生成
|
||||
/// </summary>
|
||||
public long AssemblyManifestId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 程序集实例
|
||||
/// </summary>
|
||||
public System.Reflection.Assembly Assembly { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ProtoBuf 序列化类型注册器
|
||||
/// </summary>
|
||||
internal INetworkProtocolRegistrar NetworkProtocolRegistrar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 事件系统注册器
|
||||
/// </summary>
|
||||
internal IEventSystemRegistrar EventSystemRegistrar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 实体系统注册器
|
||||
/// </summary>
|
||||
internal IEntitySystemRegistrar EntitySystemRegistrar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息分发器注册器
|
||||
/// </summary>
|
||||
internal IMessageHandlerResolver MessageHandlerResolver { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 实体类型集合注册器
|
||||
/// </summary>
|
||||
internal IEntityTypeCollectionRegistrar EntityTypeCollectionRegistrar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 网络协议 OpCode 解析器接口
|
||||
/// </summary>
|
||||
internal INetworkProtocolOpCodeResolver NetworkProtocolOpCodeResolver { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 网络协议 Response 解析器接口
|
||||
/// </summary>
|
||||
internal INetworkProtocolResponseTypeResolver NetworkProtocolResponseTypeResolver { get; set; }
|
||||
|
||||
#if FANTASY_NET
|
||||
/// <summary>
|
||||
/// 分表注册器
|
||||
/// </summary>
|
||||
internal ISeparateTableRegistrar SeparateTableRegistrar { get; set; }
|
||||
#endif
|
||||
#if FANTASY_WEBGL
|
||||
/// <summary>
|
||||
/// 程序集清单集合(WebGL 单线程版本)
|
||||
/// Key: 程序集唯一标识, Value: 程序集清单对象
|
||||
/// </summary>
|
||||
private static readonly Dictionary<long, AssemblyManifest> Manifests = new Dictionary<long, AssemblyManifest>();
|
||||
#else
|
||||
/// <summary>
|
||||
/// 程序集清单集合(线程安全版本)
|
||||
/// Key: 程序集唯一标识, Value: 程序集清单对象
|
||||
/// </summary>
|
||||
internal static readonly ConcurrentDictionary<long, AssemblyManifest> Manifests = new ConcurrentDictionary<long, AssemblyManifest>();
|
||||
#endif
|
||||
/// <summary>
|
||||
/// 清理程序集清单内部资源
|
||||
/// 释放所有注册器并清空引用
|
||||
/// </summary>
|
||||
internal void Clear()
|
||||
{
|
||||
EventSystemRegistrar?.Dispose();
|
||||
|
||||
Assembly = null;
|
||||
NetworkProtocolRegistrar = null;
|
||||
EventSystemRegistrar = null;
|
||||
EntitySystemRegistrar = null;
|
||||
MessageHandlerResolver = null;
|
||||
EntityTypeCollectionRegistrar = null;
|
||||
#if FANTASY_NET
|
||||
SeparateTableRegistrar = null;
|
||||
#endif
|
||||
}
|
||||
|
||||
#region static
|
||||
|
||||
#if FANTASY_NET
|
||||
/// <summary>
|
||||
/// 注册程序集清单
|
||||
/// 此方法由 Source Generator 生成的 ModuleInitializer 自动调用
|
||||
/// 直接创建并缓存完整的 AssemblyManifest
|
||||
/// </summary>
|
||||
/// <param name="assemblyManifestId">程序集唯一标识(通过程序集名称哈希生成)</param>
|
||||
/// <param name="assembly">程序集实例</param>
|
||||
/// <param name="networkProtocolRegistrar">网络协议注册器</param>
|
||||
/// <param name="eventSystemRegistrar">事件系统注册器</param>
|
||||
/// <param name="entitySystemRegistrar">实体系统注册器</param>
|
||||
/// <param name="messageHandlerResolver">消息分发器注册器</param>
|
||||
/// <param name="entityTypeCollectionRegistrar">实体类型集合注册器</param>
|
||||
/// <param name="separateTableRegistrar">分表注册器</param>
|
||||
/// <param name="networkProtocolOpCodeResolver">网络协议 OpCode 解析器接口</param>
|
||||
/// <param name="networkProtocolResponseTypeResolver">网络协议 Response 解析器接口</param>
|
||||
public static void Register(
|
||||
long assemblyManifestId,
|
||||
System.Reflection.Assembly assembly,
|
||||
INetworkProtocolRegistrar networkProtocolRegistrar,
|
||||
IEventSystemRegistrar eventSystemRegistrar,
|
||||
IEntitySystemRegistrar entitySystemRegistrar,
|
||||
IMessageHandlerResolver messageHandlerResolver,
|
||||
IEntityTypeCollectionRegistrar entityTypeCollectionRegistrar,
|
||||
ISeparateTableRegistrar separateTableRegistrar,
|
||||
INetworkProtocolOpCodeResolver networkProtocolOpCodeResolver,
|
||||
INetworkProtocolResponseTypeResolver networkProtocolResponseTypeResolver)
|
||||
{
|
||||
var manifest = new AssemblyManifest
|
||||
{
|
||||
Assembly = assembly,
|
||||
AssemblyManifestId = assemblyManifestId,
|
||||
NetworkProtocolRegistrar = networkProtocolRegistrar,
|
||||
EventSystemRegistrar = eventSystemRegistrar,
|
||||
EntitySystemRegistrar = entitySystemRegistrar,
|
||||
MessageHandlerResolver = messageHandlerResolver,
|
||||
EntityTypeCollectionRegistrar = entityTypeCollectionRegistrar,
|
||||
SeparateTableRegistrar = separateTableRegistrar,
|
||||
NetworkProtocolOpCodeResolver = networkProtocolOpCodeResolver,
|
||||
NetworkProtocolResponseTypeResolver = networkProtocolResponseTypeResolver
|
||||
};
|
||||
|
||||
Manifests.TryAdd(assemblyManifestId, manifest);
|
||||
AssemblyLifecycle.OnLoad(manifest).Coroutine();
|
||||
}
|
||||
#endif
|
||||
#if FANTASY_UNITY
|
||||
/// <summary>
|
||||
/// 注册程序集清单
|
||||
/// 此方法由 Source Generator 生成的 ModuleInitializer 自动调用
|
||||
/// 直接创建并缓存完整的 AssemblyManifest
|
||||
/// </summary>
|
||||
/// <param name="assemblyManifestId">程序集唯一标识(通过程序集名称哈希生成)</param>
|
||||
/// <param name="assembly">程序集实例</param>
|
||||
/// <param name="networkProtocolRegistrar">网络协议注册器</param>
|
||||
/// <param name="eventSystemRegistrar">事件系统注册器</param>
|
||||
/// <param name="entitySystemRegistrar">实体系统注册器</param>
|
||||
/// <param name="messageHandlerResolver">消息分发器注册器</param>
|
||||
/// <param name="entityTypeCollectionRegistrar">实体类型集合注册器</param>
|
||||
/// <param name="networkProtocolOpCodeResolver">网络协议 OpCode 解析器接口</param>
|
||||
/// <param name="networkProtocolResponseTypeResolver">网络协议 Response 解析器接口</param>
|
||||
public static void Register(
|
||||
long assemblyManifestId,
|
||||
System.Reflection.Assembly assembly,
|
||||
INetworkProtocolRegistrar networkProtocolRegistrar,
|
||||
IEventSystemRegistrar eventSystemRegistrar,
|
||||
IEntitySystemRegistrar entitySystemRegistrar,
|
||||
IMessageHandlerResolver messageHandlerResolver,
|
||||
IEntityTypeCollectionRegistrar entityTypeCollectionRegistrar,
|
||||
INetworkProtocolOpCodeResolver networkProtocolOpCodeResolver,
|
||||
INetworkProtocolResponseTypeResolver networkProtocolResponseTypeResolver)
|
||||
{
|
||||
var manifest = new AssemblyManifest
|
||||
{
|
||||
Assembly = assembly,
|
||||
AssemblyManifestId = assemblyManifestId,
|
||||
NetworkProtocolRegistrar = networkProtocolRegistrar,
|
||||
EventSystemRegistrar = eventSystemRegistrar,
|
||||
EntitySystemRegistrar = entitySystemRegistrar,
|
||||
MessageHandlerResolver = messageHandlerResolver,
|
||||
EntityTypeCollectionRegistrar = entityTypeCollectionRegistrar,
|
||||
NetworkProtocolOpCodeResolver = networkProtocolOpCodeResolver,
|
||||
NetworkProtocolResponseTypeResolver = networkProtocolResponseTypeResolver
|
||||
};
|
||||
#if FANTASY_WEBGL
|
||||
Manifests[assemblyManifestId] = manifest;
|
||||
#else
|
||||
Manifests.TryAdd(assemblyManifestId, manifest);
|
||||
#endif
|
||||
AssemblyLifecycle.OnLoad(manifest).Coroutine();
|
||||
}
|
||||
#endif
|
||||
/// <summary>
|
||||
/// 取消注册指定程序集的清单
|
||||
/// </summary>
|
||||
/// <param name="assemblyManifestId">程序集唯一标识</param>
|
||||
public static void Unregister(long assemblyManifestId)
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
if (Manifests.TryGetValue(assemblyManifestId, out var manifest))
|
||||
{
|
||||
AssemblyLifecycle.OnUnLoad(manifest).Coroutine();
|
||||
Manifests.Remove(assemblyManifestId);
|
||||
}
|
||||
#else
|
||||
if (Manifests.TryRemove(assemblyManifestId, out var manifest))
|
||||
{
|
||||
AssemblyLifecycle.OnUnLoad(manifest).Coroutine();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前框架注册的所有程序集清单
|
||||
/// 通过迭代器模式返回所有已注册的程序集清单对象
|
||||
/// </summary>
|
||||
public static IEnumerable<AssemblyManifest> GetAssemblyManifest
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var (_, assemblyManifest) in Manifests)
|
||||
{
|
||||
yield return assemblyManifest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放所有程序集清单资源
|
||||
/// 卸载所有已注册的程序集,触发卸载事件,清理所有注册器和生命周期回调
|
||||
/// </summary>
|
||||
/// <returns>异步任务</returns>
|
||||
public static async FTask Dispose()
|
||||
{
|
||||
foreach (var (_, assemblyManifest) in Manifests)
|
||||
{
|
||||
await AssemblyLifecycle.OnUnLoad(assemblyManifest);
|
||||
assemblyManifest.Clear();
|
||||
}
|
||||
|
||||
Manifests.Clear();
|
||||
AssemblyLifecycle.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Assembly/AssemblyManifest.cs.meta
vendored
Normal file
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Assembly/AssemblyManifest.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b3ec9cf56a12434a80505dca842a833
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Assembly/Interface.meta
vendored
Normal file
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Assembly/Interface.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21c355b8a670428478a4d839823fda39
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
29
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Assembly/Interface/IAssemblyLifecycle.cs
vendored
Normal file
29
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Assembly/Interface/IAssemblyLifecycle.cs
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using Fantasy.Async;
|
||||
|
||||
namespace Fantasy.Assembly
|
||||
{
|
||||
/// <summary>
|
||||
/// 程序集生命周期回调接口
|
||||
/// 实现此接口的类型可以接收程序集的加载、卸载、重载事件通知
|
||||
/// 通过 AssemblySystem.Add() 注册后,在程序集状态变化时会自动调用对应的生命周期方法
|
||||
/// </summary>
|
||||
internal interface IAssemblyLifecycle
|
||||
{
|
||||
/// <summary>
|
||||
/// 程序集加载或重载时调用
|
||||
/// 当新的程序集被加载到框架中时触发此回调,重新加载已存在的程序集时也会调用
|
||||
/// </summary>
|
||||
/// <param name="assemblyManifest">程序集清单对象,包含程序集的元数据和注册器</param>
|
||||
/// <returns>异步任务</returns>
|
||||
FTask OnLoad(AssemblyManifest assemblyManifest);
|
||||
|
||||
/// <summary>
|
||||
/// 程序集卸载时调用
|
||||
/// 当程序集从框架中卸载时触发此回调,应在此方法中清理该程序集相关的资源
|
||||
/// </summary>
|
||||
/// <param name="assemblyManifest">程序集清单对象,包含程序集的元数据和注册器</param>
|
||||
/// <returns>异步任务</returns>
|
||||
FTask OnUnload(AssemblyManifest assemblyManifest);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c72e8392156fd34db27c23ffce41bbb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Entitas;
|
||||
using Fantasy.Entitas.Interface;
|
||||
|
||||
namespace Fantasy.Assembly
|
||||
{
|
||||
/// <summary>
|
||||
/// 实体系统注册器接口
|
||||
/// 由 Source Generator 自动生成实现类,用于在程序集加载时注册实体系统
|
||||
/// </summary>
|
||||
public interface IEntitySystemRegistrar
|
||||
{
|
||||
#if FANTASY_NET
|
||||
/// <summary>
|
||||
/// 注册该程序集中的所有实体系统
|
||||
/// </summary>
|
||||
/// <param name="awakeSystems">Awake 系统容器</param>
|
||||
/// <param name="updateSystems">Update 系统容器</param>
|
||||
/// <param name="destroySystems">Destroy 系统容器</param>
|
||||
/// <param name="deserializeSystems">Deserialize 系统容器</param>
|
||||
void RegisterSystems(
|
||||
Dictionary<long, Action<Entity>> awakeSystems,
|
||||
Dictionary<long, Action<Entity>> updateSystems,
|
||||
Dictionary<long, Action<Entity>> destroySystems,
|
||||
Dictionary<long, Action<Entity>> deserializeSystems);
|
||||
|
||||
/// <summary>
|
||||
/// 取消注册该程序集中的所有实体系统(热重载卸载时调用)
|
||||
/// </summary>
|
||||
/// <param name="awakeSystems">Awake 系统容器</param>
|
||||
/// <param name="updateSystems">Update 系统容器</param>
|
||||
/// <param name="destroySystems">Destroy 系统容器</param>
|
||||
/// <param name="deserializeSystems">Deserialize 系统容器</param>
|
||||
void UnRegisterSystems(
|
||||
Dictionary<long, Action<Entity>> awakeSystems,
|
||||
Dictionary<long, Action<Entity>> updateSystems,
|
||||
Dictionary<long, Action<Entity>> destroySystems,
|
||||
Dictionary<long, Action<Entity>> deserializeSystems);
|
||||
#endif
|
||||
#if FANTASY_UNITY
|
||||
/// <summary>
|
||||
/// 注册该程序集中的所有实体系统
|
||||
/// </summary>
|
||||
/// <param name="awakeSystems">Awake 系统容器</param>
|
||||
/// <param name="updateSystems">Update 系统容器</param>
|
||||
/// <param name="destroySystems">Destroy 系统容器</param>
|
||||
/// <param name="deserializeSystems">Deserialize 系统容器</param>
|
||||
/// <param name="lateUpdateSystems">LateUpdate 系统容器</param>
|
||||
void RegisterSystems(
|
||||
Dictionary<long, Action<Entity>> awakeSystems,
|
||||
Dictionary<long, Action<Entity>> updateSystems,
|
||||
Dictionary<long, Action<Entity>> destroySystems,
|
||||
Dictionary<long, Action<Entity>> deserializeSystems,
|
||||
Dictionary<long, Action<Entity>> lateUpdateSystems);
|
||||
|
||||
/// <summary>
|
||||
/// 取消注册该程序集中的所有实体系统(热重载卸载时调用)
|
||||
/// </summary>
|
||||
/// <param name="awakeSystems">Awake 系统容器</param>
|
||||
/// <param name="updateSystems">Update 系统容器</param>
|
||||
/// <param name="destroySystems">Destroy 系统容器</param>
|
||||
/// <param name="deserializeSystems">Deserialize 系统容器</param>
|
||||
/// <param name="lateUpdateSystems">LateUpdate 系统容器</param>
|
||||
void UnRegisterSystems(
|
||||
Dictionary<long, Action<Entity>> awakeSystems,
|
||||
Dictionary<long, Action<Entity>> updateSystems,
|
||||
Dictionary<long, Action<Entity>> destroySystems,
|
||||
Dictionary<long, Action<Entity>> deserializeSystems,
|
||||
Dictionary<long, Action<Entity>> lateUpdateSystems);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90f03af4cb2313c4e9891773de0ba9c8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Fantasy.Assembly
|
||||
{
|
||||
/// <summary>
|
||||
/// 实体类型集合注册器接口
|
||||
/// 由 Source Generator 自动生成实现类,用于收集和提供程序集中定义的所有实体类型
|
||||
/// </summary>
|
||||
public interface IEntityTypeCollectionRegistrar
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取该程序集中定义的所有实体类型
|
||||
/// 返回继承自 Entity 的所有类型列表
|
||||
/// </summary>
|
||||
/// <returns>实体类型列表</returns>
|
||||
List<Type> GetEntityTypes();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15eca5c104b90d548b156fe72a8ab7df
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
35
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Assembly/Interface/IEventSystemRegistrar.cs
vendored
Normal file
35
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Assembly/Interface/IEventSystemRegistrar.cs
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using Fantasy.DataStructure.Collection;
|
||||
using Fantasy.Event;
|
||||
|
||||
namespace Fantasy.Assembly
|
||||
{
|
||||
/// <summary>
|
||||
/// 事件系统注册器接口
|
||||
/// 由 Source Generator 自动生成实现类,用于在程序集加载时注册事件系统
|
||||
/// </summary>
|
||||
public interface IEventSystemRegistrar : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 注册该程序集中的所有事件系统
|
||||
/// </summary>
|
||||
/// <param name="events">同步事件容器</param>
|
||||
/// <param name="asyncEvents">异步事件容器</param>
|
||||
/// <param name="sphereEvents">领域事件容器</param>
|
||||
void RegisterSystems(
|
||||
OneToManyList<RuntimeTypeHandle, IEvent> events,
|
||||
OneToManyList<RuntimeTypeHandle, IEvent> asyncEvents,
|
||||
OneToManyList<RuntimeTypeHandle, IEvent> sphereEvents);
|
||||
|
||||
/// <summary>
|
||||
/// 取消注册该程序集中的所有事件系统(热重载卸载时调用)
|
||||
/// </summary>
|
||||
/// <param name="events">同步事件容器</param>
|
||||
/// <param name="asyncEvents">异步事件容器</param>
|
||||
/// <param name="sphereEvents">领域事件容器</param>
|
||||
void UnRegisterSystems(
|
||||
OneToManyList<RuntimeTypeHandle, IEvent> events,
|
||||
OneToManyList<RuntimeTypeHandle, IEvent> asyncEvents,
|
||||
OneToManyList<RuntimeTypeHandle, IEvent> sphereEvents);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 54d9defaadec9b043bf8b3efea1f0373
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.DataStructure.Dictionary;
|
||||
using Fantasy.Entitas;
|
||||
using Fantasy.Network;
|
||||
using Fantasy.Network.Interface;
|
||||
using Fantasy.Async;
|
||||
|
||||
namespace Fantasy.Assembly
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息分发器注册器接口
|
||||
/// 由 Source Generator 自动生成实现类,用于在程序集加载时注册网络消息处理器
|
||||
/// </summary>
|
||||
public interface IMessageHandlerResolver
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
int GetMessageHandlerCount();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="session"></param>
|
||||
/// <param name="rpcId"></param>
|
||||
/// <param name="protocolCode"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <returns></returns>
|
||||
bool MessageHandler(Session session, uint rpcId, uint protocolCode, object message);
|
||||
#if FANTASY_NET
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
int GetRouteMessageHandlerCount();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="session"></param>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="rpcId"></param>
|
||||
/// <param name="protocolCode"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <returns></returns>
|
||||
FTask<bool> RouteMessageHandler(Session session, Entity entity, uint rpcId, uint protocolCode, object message);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2fa889cb07e1d344ca1b5e592c991575
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
|
||||
namespace Fantasy.Assembly
|
||||
{
|
||||
/// <summary>
|
||||
/// 网络协议 OpCode 解析器接口
|
||||
/// 用于通过生成的 switch 表达式实现高性能的 OpCode 到 Type 的解析
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 此接口由 SourceGenerator 自动生成的类实现。
|
||||
/// 每个包含网络协议的程序集都会生成自己的解析器实现。
|
||||
/// 生成的实现使用 switch 表达式而不是字典查找,以获得更好的性能。
|
||||
/// </remarks>
|
||||
public interface INetworkProtocolOpCodeResolver
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取当前OpCode数量
|
||||
/// </summary>
|
||||
/// <returns>返回对应的OpCode数量</returns>
|
||||
int GetOpCodeCount();
|
||||
/// <summary>
|
||||
/// 获取当前RouteType数量
|
||||
/// </summary>
|
||||
/// <returns>返回对应的RouteType数量</returns>
|
||||
int GetCustomRouteTypeCount();
|
||||
/// <summary>
|
||||
/// 根据指定的 OpCode 获取对应的 Type
|
||||
/// </summary>
|
||||
/// <param name="opCode">网络协议操作码</param>
|
||||
/// <returns>OpCode 对应的类型;如果未找到则返回 null</returns>
|
||||
Type GetOpCodeType(uint opCode);
|
||||
/// <summary>
|
||||
/// 根据指定的 OpCode 获取对应的 CustomRouteType
|
||||
/// </summary>
|
||||
/// <param name="opCode">网络协议操作码</param>
|
||||
/// <returns>OpCode 对应的CustomRouteType;如果未找到则返回 null</returns>
|
||||
int? GetCustomRouteType(uint opCode);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5bc0a3c95fad0b54bafc40ad4efd9df3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Fantasy.Assembly
|
||||
{
|
||||
/// <summary>
|
||||
/// NetworkProtocol 类型注册器接口
|
||||
/// 由 Source Generator 自动生成实现类,用于收集和提供程序集中需要 NetworkProtocol 序列化的类型
|
||||
/// </summary>
|
||||
public interface INetworkProtocolRegistrar
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取该程序集中需要 NetworkProtocol 序列化的所有类型
|
||||
/// 返回所有使用 NetworkProtocol 序列化特性标记的类型列表
|
||||
/// </summary>
|
||||
/// <returns>NetworkProtocol 序列化类型列表</returns>
|
||||
List<Type> GetNetworkProtocolTypes();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 455bad26fa79b644ba8415cb1ba700fd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace Fantasy.Assembly
|
||||
{
|
||||
/// <summary>
|
||||
/// 网络协议响应类型解析器接口
|
||||
/// 用于根据 OpCode 解析对应的响应消息类型
|
||||
/// 此接口通常由 NetworkProtocol SourceGenerator 自动生成实现
|
||||
/// </summary>
|
||||
public interface INetworkProtocolResponseTypeResolver
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取已注册的 Response 总数
|
||||
/// </summary>
|
||||
/// <returns>协议系统中可用的 OpCode 数量</returns>
|
||||
int GetRequestCount();
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定 OpCode 对应的响应消息类型
|
||||
/// 用于在处理网络消息时确定期望的响应类型
|
||||
/// </summary>
|
||||
/// <param name="opCode">要解析的 OpCode</param>
|
||||
/// <returns>响应消息的类型,如果该 OpCode 没有关联响应类型则返回 null</returns>
|
||||
Type GetResponseType(uint opCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 214fdb11ed5d07649973f5d6ad14a792
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,34 @@
|
||||
#if FANTASY_NET
|
||||
namespace Fantasy.Assembly;
|
||||
|
||||
/// <summary>
|
||||
/// 分表注册器接口,用于自动注册标记了 SeparateTableAttribute 的实体类型。
|
||||
/// 通过 Source Generator 自动生成实现类,替代运行时反射,提升性能。
|
||||
/// </summary>
|
||||
public interface ISeparateTableRegistrar
|
||||
{
|
||||
/// <summary>
|
||||
/// 分表信息记录,包含父实体类型、子实体类型和数据库集合名称。
|
||||
/// </summary>
|
||||
/// <param name="RootType">父实体的类型,表示子实体属于哪个父实体。</param>
|
||||
/// <param name="EntityType">子实体的类型,即标记了 SeparateTableAttribute 的实体类型。</param>
|
||||
/// <param name="TableName">在数据库中使用的集合名称。</param>
|
||||
public sealed record SeparateTableInfo(Type RootType, Type EntityType, string TableName);
|
||||
|
||||
/// <summary>
|
||||
/// 注册所有分表信息。
|
||||
/// 返回包含所有标记了 SeparateTableAttribute 的实体及其配置信息的列表。
|
||||
/// </summary>
|
||||
/// <returns>分表信息列表。</returns>
|
||||
List<SeparateTableInfo> Register();
|
||||
|
||||
/// <summary>
|
||||
/// 反注册所有分表信息。
|
||||
/// 返回需要移除的分表信息列表。
|
||||
/// </summary>
|
||||
/// <returns>分表信息列表。</returns>
|
||||
List<SeparateTableInfo> UnRegister();
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e44ea41d753b8454bba7ca941bc4e3d6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Benchmark.meta
vendored
Normal file
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Benchmark.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 40a9658e852cd49428c3c3d2495f92b7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Benchmark/Handler.meta
vendored
Normal file
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Benchmark/Handler.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 38db8c2397499654db6dd3b6c3711060
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,25 @@
|
||||
using Fantasy.Async;
|
||||
using Fantasy.InnerMessage;
|
||||
using Fantasy.Network.Interface;
|
||||
|
||||
#if FANTASY_NET
|
||||
namespace Fantasy.Network.Benchmark.Handler;
|
||||
|
||||
/// <summary>
|
||||
/// BenchmarkRequestHandler
|
||||
/// </summary>
|
||||
public sealed class BenchmarkRequestHandler : MessageRPC<BenchmarkRequest, BenchmarkResponse>
|
||||
{
|
||||
/// <summary>
|
||||
/// Run方法
|
||||
/// </summary>
|
||||
/// <param name="session"></param>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="response"></param>
|
||||
/// <param name="reply"></param>
|
||||
protected override async FTask Run(Session session, BenchmarkRequest request, BenchmarkResponse response, Action reply)
|
||||
{
|
||||
await FTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 819edfe1cb956754986be6db2e67ba07
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase.meta
vendored
Normal file
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4961a2a852e804ad69bad45492f22555
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
20
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase/DataBaseType.cs
vendored
Normal file
20
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase/DataBaseType.cs
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// ReSharper disable CheckNamespace
|
||||
// ReSharper disable InconsistentNaming
|
||||
#if FANTASY_NET
|
||||
namespace Fantasy.DataBase;
|
||||
|
||||
/// <summary>
|
||||
/// 数据库类型
|
||||
/// </summary>
|
||||
public enum DataBaseType
|
||||
{
|
||||
/// <summary>
|
||||
/// 默认
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// MongoDB
|
||||
/// </summary>
|
||||
MongoDB = 1
|
||||
}
|
||||
#endif
|
||||
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase/DataBaseType.cs.meta
vendored
Normal file
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase/DataBaseType.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: abd508d980707405bb2d5bbfee14b6b8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
210
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase/IDataBase.cs
vendored
Normal file
210
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase/IDataBase.cs
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
#if FANTASY_NET
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using Fantasy.Async;
|
||||
using Fantasy.Entitas;
|
||||
using MongoDB.Driver;
|
||||
// ReSharper disable InconsistentNaming
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
|
||||
|
||||
#pragma warning disable CS8625
|
||||
|
||||
namespace Fantasy.DataBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据库设置助手
|
||||
/// </summary>
|
||||
public static class DataBaseSetting
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化自定义委托,当设置了这个委托后,就不会自动创建MongoClient,需要自己在委托里创建MongoClient。
|
||||
/// </summary>
|
||||
public static Func<DataBaseCustomConfig, MongoClient>? MongoDBCustomInitialize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MongoDB自定义连接参数
|
||||
/// </summary>
|
||||
public sealed class DataBaseCustomConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前Scene
|
||||
/// </summary>
|
||||
public Scene Scene;
|
||||
/// <summary>
|
||||
/// 连接字符串
|
||||
/// </summary>
|
||||
public string ConnectionString;
|
||||
/// <summary>
|
||||
/// 数据库名字
|
||||
/// </summary>
|
||||
public string DBName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 表示用于执行各种数据库操作的数据库接口。
|
||||
/// </summary>
|
||||
public interface IDataBase : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 获得当前数据的类型
|
||||
/// </summary>
|
||||
public DataBaseType GetDataBaseType { get;}
|
||||
/// <summary>
|
||||
/// 获得对应数据的操作实例
|
||||
/// </summary>
|
||||
/// <returns>如MongoDB就是IMongoDatabase</returns>
|
||||
public object GetDataBaseInstance { get;}
|
||||
/// <summary>
|
||||
/// 初始化数据库连接。
|
||||
/// </summary>
|
||||
IDataBase Initialize(Scene scene, string connectionString, string dbName);
|
||||
/// <summary>
|
||||
/// 在指定的集合中检索类型 <typeparamref name="T"/> 的实体数量。
|
||||
/// </summary>
|
||||
FTask<long> Count<T>(string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 在指定的集合中检索满足给定筛选条件的类型 <typeparamref name="T"/> 的实体数量。
|
||||
/// </summary>
|
||||
FTask<long> Count<T>(Expression<Func<T, bool>> filter, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 检查指定集合中是否存在类型 <typeparamref name="T"/> 的实体。
|
||||
/// </summary>
|
||||
FTask<bool> Exist<T>(string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 检查指定集合中是否存在满足给定筛选条件的类型 <typeparamref name="T"/> 的实体。
|
||||
/// </summary>
|
||||
FTask<bool> Exist<T>(Expression<Func<T, bool>> filter, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 从指定集合中检索指定 ID 的类型 <typeparamref name="T"/> 的实体,不锁定。
|
||||
/// </summary>
|
||||
FTask<T> QueryNotLock<T>(long id, bool isDeserialize = false, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 从指定集合中检索指定 ID 的类型 <typeparamref name="T"/> 的实体。
|
||||
/// </summary>
|
||||
FTask<T> Query<T>(long id, bool isDeserialize = false, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 按页查询满足给定筛选条件的类型 <typeparamref name="T"/> 的实体数量和日期。
|
||||
/// </summary>
|
||||
FTask<(int count, List<T> dates)> QueryCountAndDatesByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, bool isDeserialize = false, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 按页查询满足给定筛选条件的类型 <typeparamref name="T"/> 的实体数量和日期。
|
||||
/// </summary>
|
||||
FTask<(int count, List<T> dates)> QueryCountAndDatesByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, string[] cols, bool isDeserialize = false, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 分页查询指定集合中满足给定筛选条件的类型 <typeparamref name="T"/> 的实体列表。
|
||||
/// </summary>
|
||||
FTask<List<T>> QueryByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, bool isDeserialize = false, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 分页查询指定集合中满足给定筛选条件的类型 <typeparamref name="T"/> 的实体列表,仅返回指定列的数据。
|
||||
/// </summary>
|
||||
FTask<List<T>> QueryByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, string[] cols, bool isDeserialize = false, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 从指定集合中按页查询满足给定筛选条件的类型 <typeparamref name="T"/> 的实体列表,按指定字段排序。
|
||||
/// </summary>
|
||||
FTask<List<T>> QueryByPageOrderBy<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, Expression<Func<T, object>> orderByExpression, bool isAsc = true, bool isDeserialize = false, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 检索满足给定筛选条件的类型 <typeparamref name="T"/> 的第一个实体,从指定集合中。
|
||||
/// </summary>
|
||||
FTask<T?> First<T>(Expression<Func<T, bool>> filter, bool isDeserialize = false, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 查询指定集合中满足给定 JSON 查询字符串的类型 <typeparamref name="T"/> 的第一个实体,仅返回指定列的数据。
|
||||
/// </summary>
|
||||
FTask<T> First<T>(string json, string[] cols, bool isDeserialize = false, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 从指定集合中按页查询满足给定筛选条件的类型 <typeparamref name="T"/> 的实体列表,按指定字段排序。
|
||||
/// </summary>
|
||||
FTask<List<T>> QueryOrderBy<T>(Expression<Func<T, bool>> filter, Expression<Func<T, object>> orderByExpression, bool isAsc = true, bool isDeserialize = false, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 从指定集合中按页查询满足给定筛选条件的类型 <typeparamref name="T"/> 的实体列表。
|
||||
/// </summary>
|
||||
FTask<List<T>> Query<T>(Expression<Func<T, bool>> filter, bool isDeserialize = false, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 查询指定集合中满足给定筛选条件的类型 <typeparamref name="T"/> 实体列表,仅返回指定字段的数据。
|
||||
/// </summary>
|
||||
FTask<List<T>> Query<T>(Expression<Func<T, bool>> filter, Expression<Func<T, object>>[] cols, bool isDeserialize = false, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 查询指定 ID 的多个集合,将结果存储在给定的实体列表中。
|
||||
/// </summary>
|
||||
FTask Query(long id, List<string> collectionNames, List<Entity> result, bool isDeserialize = false);
|
||||
/// <summary>
|
||||
/// 根据给定的 JSON 查询字符串查询指定集合中的类型 <typeparamref name="T"/> 实体列表。
|
||||
/// </summary>
|
||||
FTask<List<T>> QueryJson<T>(string json, bool isDeserialize = false, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 根据给定的 JSON 查询字符串查询指定集合中的类型 <typeparamref name="T"/> 实体列表,仅返回指定列的数据。
|
||||
/// </summary>
|
||||
FTask<List<T>> QueryJson<T>(string json, string[] cols, bool isDeserialize = false, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 根据给定的 JSON 查询字符串查询指定集合中的类型 <typeparamref name="T"/> 实体列表,通过指定的任务 ID 进行标识。
|
||||
/// </summary>
|
||||
FTask<List<T>> QueryJson<T>(long taskId, string json, bool isDeserialize = false, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 查询指定集合中满足给定筛选条件的类型 <typeparamref name="T"/> 实体列表,仅返回指定列的数据。
|
||||
/// </summary>
|
||||
FTask<List<T>> Query<T>(Expression<Func<T, bool>> filter, string[] cols, bool isDeserialize = false, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 保存类型 <typeparamref name="T"/> 实体到指定集合中,如果集合不存在将自动创建。
|
||||
/// </summary>
|
||||
FTask Save<T>(T entity, string collection = null) where T : Entity, new();
|
||||
/// <summary>
|
||||
/// 保存一组实体到数据库中,根据实体列表的 ID 进行区分和存储。
|
||||
/// </summary>
|
||||
FTask Save(long id, List<Entity> entities);
|
||||
/// <summary>
|
||||
/// 通过事务会话将类型 <typeparamref name="T"/> 实体保存到指定集合中,如果集合不存在将自动创建。
|
||||
/// </summary>
|
||||
FTask Save<T>(object transactionSession, T entity, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 向指定集合中插入一个类型 <typeparamref name="T"/> 实体,如果集合不存在将自动创建。
|
||||
/// </summary>
|
||||
FTask Insert<T>(T entity, string collection = null) where T : Entity, new();
|
||||
/// <summary>
|
||||
/// 批量插入一组类型 <typeparamref name="T"/> 实体到指定集合中,如果集合不存在将自动创建。
|
||||
/// </summary>
|
||||
FTask InsertBatch<T>(IEnumerable<T> list, string collection = null) where T : Entity, new();
|
||||
/// <summary>
|
||||
/// 通过事务会话,批量插入一组类型 <typeparamref name="T"/> 实体到指定集合中,如果集合不存在将自动创建。
|
||||
/// </summary>
|
||||
FTask InsertBatch<T>(object transactionSession, IEnumerable<T> list, string collection = null) where T : Entity, new();
|
||||
/// <summary>
|
||||
/// 通过事务会话,根据指定的 ID 从数据库中删除指定类型 <typeparamref name="T"/> 实体。
|
||||
/// </summary>
|
||||
FTask<long> Remove<T>(object transactionSession, long id, string collection = null) where T : Entity, new();
|
||||
/// <summary>
|
||||
/// 根据指定的 ID 从数据库中删除指定类型 <typeparamref name="T"/> 实体。
|
||||
/// </summary>
|
||||
FTask<long> Remove<T>(long id, string collection = null) where T : Entity, new();
|
||||
/// <summary>
|
||||
/// 通过事务会话,根据给定的筛选条件从数据库中删除指定类型 <typeparamref name="T"/> 实体。
|
||||
/// </summary>
|
||||
FTask<long> Remove<T>(long coroutineLockQueueKey, object transactionSession, Expression<Func<T, bool>> filter, string collection = null) where T : Entity, new();
|
||||
/// <summary>
|
||||
/// 根据给定的筛选条件从数据库中删除指定类型 <typeparamref name="T"/> 实体。
|
||||
/// </summary>
|
||||
FTask<long> Remove<T>(long coroutineLockQueueKey, Expression<Func<T, bool>> filter, string collection = null) where T : Entity, new();
|
||||
/// <summary>
|
||||
/// 根据给定的筛选条件计算指定集合中类型 <typeparamref name="T"/> 实体某个属性的总和。
|
||||
/// </summary>
|
||||
FTask<long> Sum<T>(Expression<Func<T, bool>> filter, Expression<Func<T, object>> sumExpression, string collection = null) where T : Entity;
|
||||
/// <summary>
|
||||
/// 在指定的集合中创建索引,以提高类型 <typeparamref name="T"/> 实体的查询性能。
|
||||
/// </summary>
|
||||
FTask CreateIndex<T>(string collection, params object[] keys) where T : Entity;
|
||||
/// <summary>
|
||||
/// 在默认集合中创建索引,以提高类型 <typeparamref name="T"/> 实体的查询性能。
|
||||
/// </summary>
|
||||
FTask CreateIndex<T>(params object[] keys) where T : Entity;
|
||||
/// <summary>
|
||||
/// 创建指定类型 <typeparamref name="T"/> 的数据库,用于存储实体。
|
||||
/// </summary>
|
||||
FTask CreateDB<T>() where T : Entity;
|
||||
/// <summary>
|
||||
/// 根据指定类型创建数据库,用于存储实体。
|
||||
/// </summary>
|
||||
FTask CreateDB(Type type);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase/IDataBase.cs.meta
vendored
Normal file
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase/IDataBase.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 83202aeeb0df040de8735030956ca861
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1081
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase/MongoDataBase.cs
vendored
Normal file
1081
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase/MongoDataBase.cs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase/MongoDataBase.cs.meta
vendored
Normal file
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase/MongoDataBase.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 366dcc82090f0466da2c2b8d0e6fa2c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
77
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase/World.cs
vendored
Normal file
77
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase/World.cs
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma warning disable CS8603 // Possible null reference return.
|
||||
#if FANTASY_NET
|
||||
using Fantasy.Platform.Net;
|
||||
|
||||
namespace Fantasy.DataBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 表示一个游戏世界。
|
||||
/// </summary>
|
||||
public sealed class World : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取游戏世界的唯一标识。
|
||||
/// </summary>
|
||||
public byte Id { get; private init; }
|
||||
/// <summary>
|
||||
/// 获取游戏世界的数据库接口。
|
||||
/// </summary>
|
||||
public IDataBase DataBase { get; private init; }
|
||||
/// <summary>
|
||||
/// 获取游戏世界的配置信息。
|
||||
/// </summary>
|
||||
public WorldConfig Config => WorldConfigData.Instance.Get(Id);
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定的配置信息创建一个游戏世界实例。
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="worldConfigId"></param>
|
||||
private World(Scene scene, byte worldConfigId)
|
||||
{
|
||||
Id = worldConfigId;
|
||||
var worldConfig = Config;
|
||||
var dbType = worldConfig.DbType.ToLower();
|
||||
|
||||
switch (dbType)
|
||||
{
|
||||
case "mongodb":
|
||||
{
|
||||
DataBase = new MongoDataBase();
|
||||
DataBase.Initialize(scene, worldConfig.DbConnection, worldConfig.DbName);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new Exception("No supported database");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个指定唯一标识的游戏世界实例。
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="id">游戏世界的唯一标识。</param>
|
||||
/// <returns>游戏世界实例。</returns>
|
||||
internal static World Create(Scene scene, byte id)
|
||||
{
|
||||
if (!WorldConfigData.Instance.TryGet(id, out var worldConfigData))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return string.IsNullOrEmpty(worldConfigData.DbConnection) ? null : new World(scene, id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放游戏世界资源。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
DataBase.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase/World.cs.meta
vendored
Normal file
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataBase/World.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f02362ef34274e508386e3cdc247927
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure.meta
vendored
Normal file
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: afe876696fb5f4779abe3289a3bf4f9f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection.meta
vendored
Normal file
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b3d477f03f404310b5ad48f89d1f193
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
346
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection/CircularBuffer.cs
vendored
Normal file
346
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection/CircularBuffer.cs
vendored
Normal file
@@ -0,0 +1,346 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
|
||||
namespace Fantasy.DataStructure.Collection
|
||||
{
|
||||
/// 环形缓存(自增式缓存,自动扩充、不会收缩缓存、所以不要用这个操作过大的IO流)
|
||||
/// 1、环大小8192,溢出的会自动增加环的大小。
|
||||
/// 2、每个块都是一个环形缓存,当溢出的时候会自动添加到下一个环中。
|
||||
/// 3、当读取完成后用过的环会放在缓存中,不会销毁掉。
|
||||
/// <summary>
|
||||
/// 自增式缓存类,继承自 Stream 和 IDisposable 接口。
|
||||
/// 环形缓存具有自动扩充的特性,但不会收缩,适用于操作不过大的 IO 流。
|
||||
/// </summary>
|
||||
public sealed class CircularBuffer : Stream, IDisposable
|
||||
{
|
||||
private byte[] _lastBuffer;
|
||||
/// <summary>
|
||||
/// 环形缓存块的默认大小
|
||||
/// </summary>
|
||||
public const int ChunkSize = 8192;
|
||||
private readonly Queue<byte[]> _bufferCache = new Queue<byte[]>();
|
||||
private readonly Queue<byte[]> _bufferQueue = new Queue<byte[]>();
|
||||
/// <summary>
|
||||
/// 获取或设置环形缓存的第一个索引位置
|
||||
/// </summary>
|
||||
public int FirstIndex { get; set; }
|
||||
/// <summary>
|
||||
/// 获取或设置环形缓存的最后一个索引位置
|
||||
/// </summary>
|
||||
public int LastIndex { get; set; }
|
||||
/// <summary>
|
||||
/// 获取环形缓存的总长度
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_bufferQueue.Count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (_bufferQueue.Count - 1) * ChunkSize + LastIndex - FirstIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取环形缓存的第一个块
|
||||
/// </summary>
|
||||
public byte[] First
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_bufferQueue.Count == 0)
|
||||
{
|
||||
AddLast();
|
||||
}
|
||||
|
||||
return _bufferQueue.Peek();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取环形缓存的最后一个块
|
||||
/// </summary>
|
||||
public byte[] Last
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_bufferQueue.Count == 0)
|
||||
{
|
||||
AddLast();
|
||||
}
|
||||
|
||||
return _lastBuffer;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 向环形缓存中添加一个新的块
|
||||
/// </summary>
|
||||
public void AddLast()
|
||||
{
|
||||
var buffer = _bufferCache.Count > 0 ? _bufferCache.Dequeue() : new byte[ChunkSize];
|
||||
_bufferQueue.Enqueue(buffer);
|
||||
_lastBuffer = buffer;
|
||||
}
|
||||
/// <summary>
|
||||
/// 从环形缓存中移除第一个块
|
||||
/// </summary>
|
||||
public void RemoveFirst()
|
||||
{
|
||||
_bufferCache.Enqueue(_bufferQueue.Dequeue());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从流中读取指定数量的数据到缓存。
|
||||
/// </summary>
|
||||
/// <param name="stream">源数据流。</param>
|
||||
/// <param name="count">要读取的字节数。</param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存中读取指定数量的数据到内存。
|
||||
/// </summary>
|
||||
/// <param name="memory">目标内存。</param>
|
||||
/// <param name="count">要读取的字节数。</param>
|
||||
public void Read(Memory<byte> memory, int count)
|
||||
{
|
||||
if (count > Length)
|
||||
{
|
||||
throw new Exception($"bufferList length < count, {Length} {count}");
|
||||
}
|
||||
|
||||
var copyCount = 0;
|
||||
while (copyCount < count)
|
||||
{
|
||||
var n = count - copyCount;
|
||||
var asMemory = First.AsMemory();
|
||||
|
||||
if (ChunkSize - FirstIndex > n)
|
||||
{
|
||||
var slice = asMemory.Slice(FirstIndex, n);
|
||||
slice.CopyTo(memory.Slice(copyCount, n));
|
||||
FirstIndex += n;
|
||||
copyCount += n;
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = ChunkSize - FirstIndex;
|
||||
var slice = asMemory.Slice(FirstIndex, length);
|
||||
slice.CopyTo(memory.Slice(copyCount, length));
|
||||
copyCount += ChunkSize - FirstIndex;
|
||||
FirstIndex = 0;
|
||||
RemoveFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从自定义流中读取数据到指定的缓冲区。
|
||||
/// </summary>
|
||||
/// <param name="buffer">目标缓冲区,用于存储读取的数据。</param>
|
||||
/// <param name="offset">目标缓冲区中的起始偏移量。</param>
|
||||
/// <param name="count">要读取的字节数。</param>
|
||||
/// <returns>实际读取的字节数。</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将数据从给定的字节数组写入流中。
|
||||
/// </summary>
|
||||
/// <param name="buffer">包含要写入的数据的字节数组。</param>
|
||||
public void Write(byte[] buffer)
|
||||
{
|
||||
Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将数据从给定的流写入流中。
|
||||
/// </summary>
|
||||
/// <param name="stream">包含要写入的数据的流。</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将数据从给定的字节数组写入流中。
|
||||
/// </summary>
|
||||
/// <param name="buffer">包含要写入的数据的字节数组。</param>
|
||||
/// <param name="offset">开始写入的缓冲区中的索引。</param>
|
||||
/// <param name="count">要写入的字节数。</param>
|
||||
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, Last, LastIndex, ChunkSize - LastIndex);
|
||||
copyCount += ChunkSize - LastIndex;
|
||||
LastIndex = ChunkSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,指示流是否支持读取操作。
|
||||
/// </summary>
|
||||
public override bool CanRead { get; } = true;
|
||||
/// <summary>
|
||||
/// 获取一个值,指示流是否支持寻找操作。
|
||||
/// </summary>
|
||||
public override bool CanSeek { get; } = false;
|
||||
/// <summary>
|
||||
/// 获取一个值,指示流是否支持写入操作。
|
||||
/// </summary>
|
||||
public override bool CanWrite { get; } = true;
|
||||
/// <summary>
|
||||
/// 获取或设置流中的位置。
|
||||
/// </summary>
|
||||
public override long Position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 刷新流(在此实现中引发未实现异常)。
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在流中寻找特定位置(在此实现中引发未实现异常)。
|
||||
/// </summary>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置流的长度(在此实现中引发未实现异常)。
|
||||
/// </summary>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放 CustomStream 使用的所有资源。
|
||||
/// </summary>
|
||||
public new void Dispose()
|
||||
{
|
||||
_bufferQueue.Clear();
|
||||
_lastBuffer = null;
|
||||
FirstIndex = 0;
|
||||
LastIndex = 0;
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 38264758f23d1448a975efc4f4b7da63
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,197 @@
|
||||
#if !FANTASY_WEBGL
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Fantasy.Pool;
|
||||
|
||||
#pragma warning disable CS8603 // Possible null reference return.
|
||||
|
||||
namespace Fantasy.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 并发的一对多列表池,用于维护具有相同键的多个值的关联关系,实现了 <see cref="IDisposable"/> 接口。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">关键字的类型,不能为空。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
public class ConcurrentOneToManyListPool<TKey, TValue> : ConcurrentOneToManyList<TKey, TValue>, IDisposable, IPool where TKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="ConcurrentOneToManyListPool{TKey, TValue}"/> 的实例。
|
||||
/// </summary>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static ConcurrentOneToManyListPool<TKey, TValue> Create()
|
||||
{
|
||||
var a = MultiThreadPool.Rent<ConcurrentOneToManyListPool<TKey, TValue>>();
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例占用的资源。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
// 清空实例的数据
|
||||
Clear();
|
||||
// 将实例返回到池中以便重用
|
||||
MultiThreadPool.Return(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 并发的一对多列表,用于维护具有相同键的多个值的关联关系。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">关键字的类型,不能为空。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="ConcurrentOneToManyList{TKey, TValue}"/> 类的新实例。
|
||||
/// </summary>
|
||||
public ConcurrentOneToManyList()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public ConcurrentOneToManyList(int recyclingLimit)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断指定键的列表是否包含指定值。
|
||||
/// </summary>
|
||||
/// <param name="key">要搜索的键。</param>
|
||||
/// <param name="value">要搜索的值。</param>
|
||||
/// <returns>如果列表包含值,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向指定键的列表中添加一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要添加值的键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键的列表中的第一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要获取第一个值的键。</param>
|
||||
/// <returns>指定键的列表中的第一个值,如果不存在则为默认值。</returns>
|
||||
public TValue First(TKey key)
|
||||
{
|
||||
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定键的列表中移除一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除值的键。</param>
|
||||
/// <param name="value">要移除的值。</param>
|
||||
public void RemoveValue(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
list.Remove(value);
|
||||
|
||||
if (list.Count == 0) RemoveKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定键以及其关联的列表。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryRemove(key, out var list)) return;
|
||||
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从队列中获取一个列表,如果队列为空则创建一个新的列表。
|
||||
/// </summary>
|
||||
/// <returns>获取的列表。</returns>
|
||||
private List<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将一个列表回收到队列中。
|
||||
/// </summary>
|
||||
/// <param name="list">要回收的列表。</param>
|
||||
private void Recycle(List<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空当前类的数据,包括从基类继承的数据以及自定义的数据队列。
|
||||
/// </summary>
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21b2d3135b133441aa08c5eb1768a103
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,194 @@
|
||||
#if !FANTASY_WEBGL
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Pool;
|
||||
|
||||
#pragma warning disable CS8603
|
||||
|
||||
namespace Fantasy.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 表示一个并发的一对多队列池,用于维护具有相同键的多个值的关联关系,实现了 <see cref="IDisposable"/> 接口。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">关键字的类型,不能为空。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
public class ConcurrentOneToManyQueuePool<TKey, TValue> : ConcurrentOneToManyQueue<TKey, TValue>, IDisposable, IPool where TKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建并返回一个 <see cref="ConcurrentOneToManyQueuePool{TKey, TValue}"/> 的实例。
|
||||
/// </summary>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static ConcurrentOneToManyQueuePool<TKey, TValue> Create()
|
||||
{
|
||||
var a = MultiThreadPool.Rent<ConcurrentOneToManyQueuePool<TKey, TValue>>();
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放当前实例所占用的资源,并将实例返回到对象池中,以便重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
// 将实例返回到对象池中,以便重用
|
||||
MultiThreadPool.Return(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个并发的一对多队列,用于维护具有相同键的多个值的关联关系。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">关键字的类型,不能为空。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断指定键的队列是否包含指定值。
|
||||
/// </summary>
|
||||
/// <param name="key">要搜索的键。</param>
|
||||
/// <param name="value">要搜索的值。</param>
|
||||
/// <returns>如果队列包含值,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向指定键的队列中添加一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要添加值的键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定键的队列中出队并返回一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要出队的键。</param>
|
||||
/// <returns>出队的值,如果队列为空则为默认值。</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试从指定键的队列中出队一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要出队的键。</param>
|
||||
/// <param name="value">出队的值,如果队列为空则为默认值。</param>
|
||||
/// <returns>如果成功出队,则为 true;否则为 false。</returns>
|
||||
public bool TryDequeue(TKey key, out TValue value)
|
||||
{
|
||||
value = Dequeue(key);
|
||||
|
||||
return value != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定键以及其关联的队列。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
TryRemove(key, out _);
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从队列中获取一个新的队列,如果队列为空则创建一个新的队列。
|
||||
/// </summary>
|
||||
/// <returns>获取的队列。</returns>
|
||||
private Queue<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new Queue<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将一个队列回收到队列池中。
|
||||
/// </summary>
|
||||
/// <param name="list">要回收的队列。</param>
|
||||
private void Recycle(Queue<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空当前类的数据,包括从基类继承的键值对字典中的数据以及自定义的队列池。
|
||||
/// </summary>
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50a0f004d8b7b4075a4ce01376e49375
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
134
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection/HashSetPool.cs
vendored
Normal file
134
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection/HashSetPool.cs
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Pool;
|
||||
|
||||
namespace Fantasy.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 可释放的哈希集合对象池。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">哈希集合中元素的类型。</typeparam>
|
||||
public sealed class HashSetPool<T> : HashSet<T>, IDisposable, IPool
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例所占用的资源,并将实例返回到对象池中,以便重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<HashSetPool<T>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="HashSetPool{T}"/> 哈希集合池的实例。
|
||||
/// </summary>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static HashSetPool<T> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var list = Pool<HashSetPool<T>>.Rent();
|
||||
list._isDispose = false;
|
||||
list._isPool = true;
|
||||
return list;
|
||||
#else
|
||||
var list = MultiThreadPool.Rent<HashSetPool<T>>();
|
||||
list._isDispose = false;
|
||||
list._isPool = true;
|
||||
return list;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基本哈希集合对象池,他自持有实际的哈希集合。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">哈希集合中元素的类型。</typeparam>
|
||||
public sealed class HashSetBasePool<T> : IDisposable, IPool
|
||||
{
|
||||
private bool _isPool;
|
||||
|
||||
/// <summary>
|
||||
/// 存储实际的哈希集合
|
||||
/// </summary>
|
||||
public HashSet<T> Set = new HashSet<T>();
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="HashSetBasePool{T}"/> 基本哈希集合对象池的实例。
|
||||
/// </summary>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static HashSetBasePool<T> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var hashSetBasePool = Pool<HashSetBasePool<T>>.Rent();
|
||||
hashSetBasePool._isPool = true;
|
||||
return hashSetBasePool;
|
||||
#else
|
||||
var hashSetBasePool = MultiThreadPool.Rent<HashSetBasePool<T>>();
|
||||
hashSetBasePool._isPool = true;
|
||||
return hashSetBasePool;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例所占用的资源,并将实例返回到对象池中,以便重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Set.Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<HashSetBasePool<T>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c49468bddc6524c419ace92b9ad68a5d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
101
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection/ListPool.cs
vendored
Normal file
101
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection/ListPool.cs
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Pool;
|
||||
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
|
||||
namespace Fantasy.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 可释放的列表(List)对象池。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">列表中元素的类型。</typeparam>
|
||||
public sealed class ListPool<T> : List<T>, IDisposable, IPool
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例所占用的资源,并将实例返回到对象池中,以便重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<ListPool<T>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定的元素创建一个 <see cref="ListPool{T}"/> 列表(List)对象池的实例。
|
||||
/// </summary>
|
||||
/// <param name="args">要添加到列表的元素。</param>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static ListPool<T> Create(params T[] args)
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var list = Pool<ListPool<T>>.Rent();
|
||||
#else
|
||||
var list = MultiThreadPool.Rent<ListPool<T>>();
|
||||
#endif
|
||||
list._isDispose = false;
|
||||
list._isPool = true;
|
||||
|
||||
if (args != null)
|
||||
{
|
||||
list.AddRange(args);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定的列表创建一个 <see cref="ListPool{T}"/> 列表(List)对象池的实例。
|
||||
/// </summary>
|
||||
/// <param name="args">要添加到列表的元素列表。</param>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static ListPool<T> Create(List<T> args)
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var list = Pool<ListPool<T>>.Rent();
|
||||
#else
|
||||
var list = MultiThreadPool.Rent<ListPool<T>>();
|
||||
#endif
|
||||
list._isDispose = false;
|
||||
list._isPool = true;
|
||||
|
||||
if (args != null)
|
||||
{
|
||||
list.AddRange(args);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection/ListPool.cs.meta
vendored
Normal file
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection/ListPool.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f629886e3686046c6b00225679a7dbf5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,208 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Pool;
|
||||
|
||||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
|
||||
namespace Fantasy.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 一对多哈希集合(OneToManyHashSet)对象池。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
public class OneToManyHashSetPool<TKey, TValue> : OneToManyHashSet<TKey, TValue>, IDisposable, IPool where TKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="OneToManyHashSetPool{TKey, TValue}"/> 一对多哈希集合(OneToManyHashSet)对象池的实例。
|
||||
/// </summary>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static OneToManyHashSetPool<TKey, TValue> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var a = Pool<OneToManyHashSetPool<TKey, TValue>>.Rent();
|
||||
#else
|
||||
var a = MultiThreadPool.Rent<OneToManyHashSetPool<TKey, TValue>>();
|
||||
#endif
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例所占用的资源,并将实例返回到对象池中,以便重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<OneToManyHashSetPool<TKey, TValue>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 一对多哈希集合(OneToManyHashSet),用于创建和管理键对应多个值的集合。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
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>();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="OneToManyHashSet{TKey, TValue}"/> 类的新实例。
|
||||
/// </summary>
|
||||
public OneToManyHashSet() { }
|
||||
|
||||
/// <summary>
|
||||
/// 设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public OneToManyHashSet(int recyclingLimit)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断指定的键值对是否存在于集合中。
|
||||
/// </summary>
|
||||
/// <param name="key">键。</param>
|
||||
/// <param name="value">值。</param>
|
||||
/// <returns>如果存在则为 true,否则为 false。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加指定的键值对到集合中。
|
||||
/// </summary>
|
||||
/// <param name="key">键。</param>
|
||||
/// <param name="value">值。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从集合中移除指定键对应的值。
|
||||
/// </summary>
|
||||
/// <param name="key">键。</param>
|
||||
/// <param name="value">要移除的值。</param>
|
||||
public void RemoveValue(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
list.Remove(value);
|
||||
|
||||
if (list.Count == 0) RemoveKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从集合中移除指定键及其对应的值集合。
|
||||
/// </summary>
|
||||
/// <param name="key">键。</param>
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
Remove(key);
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键对应的值集合,如果不存在则返回一个空的哈希集合。
|
||||
/// </summary>
|
||||
/// <param name="key">键。</param>
|
||||
/// <returns>对应的值集合或空的哈希集合。</returns>
|
||||
public HashSet<TValue> GetValue(TKey key)
|
||||
{
|
||||
if (TryGetValue(key, out HashSet<TValue> value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return _empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从队列中获取一个空闲的值集合,或者创建一个新的。
|
||||
/// </summary>
|
||||
/// <returns>值集合。</returns>
|
||||
private HashSet<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new HashSet<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回收值集合到队列中,以便重复利用。
|
||||
/// </summary>
|
||||
/// <param name="list">要回收的值集合。</param>
|
||||
private void Recycle(HashSet<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空集合中的数据并和队列。
|
||||
/// </summary>
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e0cac58c09d6429faba279d03ef2317
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
232
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection/OneToManyListPool.cs
vendored
Normal file
232
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection/OneToManyListPool.cs
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Fantasy.Pool;
|
||||
|
||||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
#pragma warning disable CS8603 // Possible null reference return.
|
||||
|
||||
namespace Fantasy.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 可回收的、一对多关系的列表池。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
public class OneToManyListPool<TKey, TValue> : OneToManyList<TKey, TValue>, IDisposable, IPool where TKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="OneToManyListPool{TKey, TValue}"/> 一对多关系的列表池的实例。
|
||||
/// </summary>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static OneToManyListPool<TKey, TValue> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL || FANTASY_EXPORTER
|
||||
var list = Pool<OneToManyListPool<TKey, TValue>>.Rent();
|
||||
#else
|
||||
var list = MultiThreadPool.Rent<OneToManyListPool<TKey, TValue>>();
|
||||
#endif
|
||||
list._isDispose = false;
|
||||
list._isPool = true;
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放当前对象所占用的资源,并将对象回收到对象池中。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL || FANTASY_EXPORTER
|
||||
Pool<OneToManyListPool<TKey, TValue>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 一对多关系的列表字典。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
public class OneToManyList<TKey, TValue> : Dictionary<TKey, List<TValue>> where TKey : notnull
|
||||
{
|
||||
private readonly int _recyclingLimit = 120;
|
||||
private static readonly List<TValue> Empty = new List<TValue>();
|
||||
private readonly Queue<List<TValue>> _queue = new Queue<List<TValue>>();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化一个新的 <see cref="OneToManyList{TKey, TValue}"/> 实例。
|
||||
/// </summary>
|
||||
public OneToManyList() { }
|
||||
|
||||
/// <summary>
|
||||
/// 设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public OneToManyList(int recyclingLimit)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断给定的键和值是否存在于列表中。
|
||||
/// </summary>
|
||||
/// <param name="key">要搜索的键。</param>
|
||||
/// <param name="value">要搜索的值。</param>
|
||||
/// <returns>如果存在则为 <see langword="true"/>,否则为 <see langword="false"/>。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向列表中添加指定键和值。
|
||||
/// </summary>
|
||||
/// <param name="key">要添加值的键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键对应的列表中的第一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要获取值的键。</param>
|
||||
/// <returns>键对应的列表中的第一个值。</returns>
|
||||
public TValue First(TKey key)
|
||||
{
|
||||
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从列表中移除指定键和值。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除值的键。</param>
|
||||
/// <param name="value">要移除的值。</param>
|
||||
/// <returns>如果成功移除则为 <see langword="true"/>,否则为 <see langword="false"/>。</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从列表中移除指定键及其关联的所有值。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
/// <returns>如果成功移除则为 <see langword="true"/>,否则为 <see langword="false"/>。</returns>
|
||||
public bool RemoveByKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Remove(key);
|
||||
Recycle(list);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键关联的所有值的列表。
|
||||
/// </summary>
|
||||
/// <param name="key">要获取值的键。</param>
|
||||
/// <returns>键关联的所有值的列表。</returns>
|
||||
public List<TValue> GetValues(TKey key)
|
||||
{
|
||||
if (TryGetValue(key, out List<TValue> list))
|
||||
{
|
||||
return list;
|
||||
}
|
||||
|
||||
return Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除字典中的所有键值对,并回收相关的值集合。
|
||||
/// </summary>
|
||||
public new void Clear()
|
||||
{
|
||||
foreach (var keyValuePair in this) Recycle(keyValuePair.Value);
|
||||
|
||||
base.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从空闲值集合队列中获取一个值集合,如果队列为空则创建一个新的值集合。
|
||||
/// </summary>
|
||||
/// <returns>从队列中获取的值集合。</returns>
|
||||
private List<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回收一个不再使用的值集合到空闲值集合队列中。
|
||||
/// </summary>
|
||||
/// <param name="list">要回收的值集合。</param>
|
||||
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: 2c48383c842aa40b683f03b79f845932
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
204
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection/OneToManyQueuePool.cs
vendored
Normal file
204
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection/OneToManyQueuePool.cs
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Pool;
|
||||
|
||||
#pragma warning disable CS8603
|
||||
|
||||
namespace Fantasy.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 支持一对多关系的队列池,用于存储具有相同键的值的队列集合。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
public class OneToManyQueuePool<TKey, TValue> : OneToManyQueue<TKey, TValue>, IDisposable, IPool where TKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="OneToManyQueuePool{TKey, TValue}"/> 一对多关系的队列池的实例。
|
||||
/// </summary>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static OneToManyQueuePool<TKey, TValue> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var a = Pool<OneToManyQueuePool<TKey, TValue>>.Rent();
|
||||
#else
|
||||
var a = MultiThreadPool.Rent<OneToManyQueuePool<TKey, TValue>>();
|
||||
#endif
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放当前实例所占用的资源,并将实例回收到对象池中。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<OneToManyQueuePool<TKey, TValue>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 支持一对多关系的队列,用于存储具有相同键的值的队列集合。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
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>
|
||||
/// 创建一个 <see cref="OneToManyQueue{TKey, TValue}"/> 一对多关系的队列的实例。设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public OneToManyQueue(int recyclingLimit = 0)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断指定键的值队列是否包含指定的值。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找的键。</param>
|
||||
/// <param name="value">要查找的值。</param>
|
||||
/// <returns>如果存在,则为 <c>true</c>;否则为 <c>false</c>。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定的值添加到指定键的值队列中。
|
||||
/// </summary>
|
||||
/// <param name="key">要添加值的键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定键的值队列中出队一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要出队的键。</param>
|
||||
/// <returns>出队的值。</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试从指定键的值队列中出队一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要出队的键。</param>
|
||||
/// <param name="value">出队的值。</param>
|
||||
/// <returns>如果成功出队,则为 <c>true</c>;否则为 <c>false</c>。</returns>
|
||||
public bool TryDequeue(TKey key, out TValue value)
|
||||
{
|
||||
value = Dequeue(key);
|
||||
|
||||
return value != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定键及其对应的值队列。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
Remove(key);
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从队列池中获取一个值队列。如果队列池为空,则创建一个新的值队列。
|
||||
/// </summary>
|
||||
/// <returns>获取的值队列。</returns>
|
||||
private Queue<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new Queue<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回收一个不再使用的值队列到队列池中,以便重用。
|
||||
/// </summary>
|
||||
/// <param name="list">要回收的值队列。</param>
|
||||
private void Recycle(Queue<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空当前实例的数据,同时回收所有值队列。
|
||||
/// </summary>
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b6455b3359394987af4c0a4855189e9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
69
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection/ReuseList.cs
vendored
Normal file
69
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection/ReuseList.cs
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Pool;
|
||||
|
||||
namespace Fantasy.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 可重用的列表,继承自 <see cref="List{T}"/> 类。该类支持通过对象池重用列表实例,以减少对象分配和释放的开销。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">列表中元素的类型。</typeparam>
|
||||
public sealed class ReuseList<T> : List<T>, IDisposable, IPool
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="ReuseList{T}"/> 可重用的列表的实例。
|
||||
/// </summary>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static ReuseList<T> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var list = Pool<ReuseList<T>>.Rent();
|
||||
#else
|
||||
var list = MultiThreadPool.Rent<ReuseList<T>>();
|
||||
#endif
|
||||
list._isDispose = false;
|
||||
list._isPool = true;
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放该实例所占用的资源,并将实例返回到对象池中,以便重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<ReuseList<T>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection/ReuseList.cs.meta
vendored
Normal file
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Collection/ReuseList.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9826877496b99479892a7359519298d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,226 @@
|
||||
#if !FANTASY_WEBGL
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Fantasy.Pool;
|
||||
|
||||
#pragma warning disable CS8603
|
||||
|
||||
namespace Fantasy.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 基于排序字典和并发集合实现的一对多映射列表的对象池包装类,继承自 <see cref="SortedConcurrentOneToManyList{TKey, TValue}"/> 类,
|
||||
/// 同时实现了 <see cref="IDisposable"/> 接口,以支持对象的重用和释放。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
public class SortedConcurrentOneToManyListPool<TKey, TValue> : SortedConcurrentOneToManyList<TKey, TValue>, IDisposable, IPool where TKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="SortedConcurrentOneToManyListPool{TKey, TValue}"/> 实例,使用默认的参数设置。
|
||||
/// </summary>
|
||||
/// <returns>新创建的 <see cref="SortedConcurrentOneToManyListPool{TKey, TValue}"/> 实例。</returns>
|
||||
public static SortedConcurrentOneToManyListPool<TKey, TValue> Create()
|
||||
{
|
||||
var a = MultiThreadPool.Rent<SortedConcurrentOneToManyListPool<TKey, TValue>>();
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放当前对象池实例,将其返回到对象池以供重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
MultiThreadPool.Return(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基于排序字典和并发集合实现的一多对映射列表类,继承自 <see cref="SortedDictionary{TKey, TValue}"/> 类,
|
||||
/// 用于在多个值与一个键关联的情况下进行管理和存储。该类支持并发操作,适用于多线程环境。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化一个新的 <see cref="SortedConcurrentOneToManyList{TKey, TValue}"/> 类的实例,使用默认的参数设置。
|
||||
/// </summary>
|
||||
public SortedConcurrentOneToManyList()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化一个新的 <see cref="SortedConcurrentOneToManyList{TKey, TValue}"/> 类的实例,指定最大缓存数量。
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public SortedConcurrentOneToManyList(int recyclingLimit = 0)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查指定的键和值是否存在于映射列表中。
|
||||
/// </summary>
|
||||
/// <param name="key">要检查的键。</param>
|
||||
/// <param name="value">要检查的值。</param>
|
||||
/// <returns>如果存在,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定的值添加到与指定键关联的列表中。
|
||||
/// </summary>
|
||||
/// <param name="key">要关联值的键。</param>
|
||||
/// <param name="value">要添加到列表的值。</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取与指定键关联的列表中的第一个值。
|
||||
/// 如果列表不存在或为空,则返回默认值。
|
||||
/// </summary>
|
||||
/// <param name="key">要获取第一个值的键。</param>
|
||||
/// <returns>第一个值,或默认值。</returns>
|
||||
public TValue First(TKey key)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从与指定键关联的列表中移除指定的值。
|
||||
/// 如果列表不存在或值不存在于列表中,则不执行任何操作。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除值的键。</param>
|
||||
/// <param name="value">要移除的值。</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从映射列表中移除指定的键及其关联的列表。
|
||||
/// 如果键不存在于映射列表中,则不执行任何操作。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
Remove(key);
|
||||
|
||||
Recycle(list);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存中获取一个可重用的列表。如果缓存中不存在列表,则创建一个新的列表并返回。
|
||||
/// </summary>
|
||||
/// <returns>可重用的列表。</returns>
|
||||
private List<TValue> Fetch()
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将不再使用的列表回收到缓存中,以便重复利用。如果缓存数量超过限制,则丢弃列表而不进行回收。
|
||||
/// </summary>
|
||||
/// <param name="list">要回收的列表。</param>
|
||||
private void Recycle(List<TValue> list)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空映射列表以及队列。
|
||||
/// </summary>
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fef1d9dea81914fcf9fc9f5b1e5989d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,192 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Pool;
|
||||
|
||||
namespace Fantasy.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 基于排序字典实现的一对多关系的映射哈希集合的对象池包装类,将唯一键映射到多个值的哈希集合。
|
||||
/// 同时实现了 <see cref="IDisposable"/> 接口,以支持对象的重用和释放。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">哈希集合中值的类型。</typeparam>
|
||||
public class SortedOneToManyHashSetPool<TKey, TValue> : SortedOneToManyHashSet<TKey, TValue>, IDisposable, IPool where TKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="SortedOneToManyHashSetPool{TKey, TValue}"/> 实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的实例。</returns>
|
||||
public static SortedOneToManyHashSetPool<TKey, TValue> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var a = Pool<SortedOneToManyHashSetPool<TKey, TValue>>.Rent();
|
||||
#else
|
||||
var a = MultiThreadPool.Rent<SortedOneToManyHashSetPool<TKey, TValue>>();
|
||||
#endif
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放当前对象池实例,将其返回到对象池以供重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<SortedOneToManyHashSetPool<TKey, TValue>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基于排序字典实现的一对多关系的映射哈希集合类,将唯一键映射到多个值的哈希集合。
|
||||
/// 用于在多个值与一个键关联的情况下进行管理和存储。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">集合中值的类型。</typeparam>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="SortedOneToManyHashSet{TKey, TValue}"/> 实例。
|
||||
/// </summary>
|
||||
public SortedOneToManyHashSet() { }
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="SortedOneToManyHashSet{TKey, TValue}"/> 实例,设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public SortedOneToManyHashSet(int recyclingLimit)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断哈希集合中是否包含指定的键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找的键。</param>
|
||||
/// <param name="value">要查找的值。</param>
|
||||
/// <returns>如果键值对存在,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定值添加到给定键关联的哈希集合中。
|
||||
/// </summary>
|
||||
/// <param name="key">要添加值的键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定键关联的哈希集合中移除特定值。
|
||||
/// 如果哈希集合不存在或值不存在于集合中,则不执行任何操作。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除值的键。</param>
|
||||
/// <param name="value">要移除的值。</param>
|
||||
public void RemoveValue(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
list.Remove(value);
|
||||
|
||||
if (list.Count == 0) RemoveKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定键以及关联的哈希集合,并将集合进行回收。
|
||||
/// 如果键不存在于映射列表中,则不执行任何操作。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
Remove(key);
|
||||
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个空的或回收的哈希集合。
|
||||
/// </summary>
|
||||
/// <returns>获取的哈希集合实例。</returns>
|
||||
private HashSet<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new HashSet<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回收一个哈希集合,将其清空并放入回收队列中。
|
||||
/// </summary>
|
||||
/// <param name="list">要回收的哈希集合。</param>
|
||||
private void Recycle(HashSet<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写 Clear 方法,清空字典并清空回收队列。
|
||||
/// </summary>
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4fe9032846c3f4a11be30459a9e12d7a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,217 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Fantasy.Pool;
|
||||
|
||||
#pragma warning disable CS8603
|
||||
|
||||
namespace Fantasy.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 基于排序字典实现的一对多映射列表的对象池包装类,继承自 <see cref="SortedOneToManyList{TKey, TValue}"/> 类,
|
||||
/// 同时实现了 <see cref="IDisposable"/> 接口,以支持对象的重用和释放。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">列表中值的类型。</typeparam>
|
||||
public class SortedOneToManyListPool<TKey, TValue> : SortedOneToManyList<TKey, TValue>, IDisposable, IPool where TKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="SortedOneToManyListPool{TKey, TValue}"/> 实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的实例。</returns>
|
||||
public static SortedOneToManyListPool<TKey, TValue> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var a = Pool<SortedOneToManyListPool<TKey, TValue>>.Rent();
|
||||
#else
|
||||
var a = MultiThreadPool.Rent<SortedOneToManyListPool<TKey, TValue>>();
|
||||
#endif
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放当前对象池实例,将其返回到对象池以供重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<SortedOneToManyListPool<TKey, TValue>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基于排序字典实现的一对多关系的映射列表类,将唯一键映射到包含多个值的列表。
|
||||
/// 用于在多个值与一个键关联的情况下进行管理和存储。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">列表中值的类型。</typeparam>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="SortedOneToManyList{TKey, TValue}"/> 实例。
|
||||
/// </summary>
|
||||
public SortedOneToManyList()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="SortedOneToManyList{TKey, TValue}"/> 实例,设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public SortedOneToManyList(int recyclingLimit = 0)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断列表中是否包含指定的键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找的键。</param>
|
||||
/// <param name="value">要查找的值。</param>
|
||||
/// <returns>如果键值对存在,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定值添加到给定键关联的列表中。
|
||||
/// </summary>
|
||||
/// <param name="key">要添加值的键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键关联的列表中的第一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找值的键。</param>
|
||||
/// <returns>指定键关联的列表中的第一个值,如果列表为空则返回默认值。</returns>
|
||||
public TValue First(TKey key)
|
||||
{
|
||||
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定键关联的列表中移除特定值。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除值的键。</param>
|
||||
/// <param name="value">要移除的值。</param>
|
||||
|
||||
public void RemoveValue(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
list.Remove(value);
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
RemoveKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定键以及关联的列表,并将列表进行回收。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Remove(key);
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个空的或回收的列表。
|
||||
/// </summary>
|
||||
/// <returns>获取的列表实例。</returns>
|
||||
private List<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回收一个列表,将其清空并放入回收队列中。如果缓存数量超过限制,则丢弃列表而不进行回收
|
||||
/// </summary>
|
||||
/// <param name="list">要回收的列表。</param>
|
||||
private void Recycle(List<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写 Clear 方法,清空字典并清空回收队列。
|
||||
/// </summary>
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e0621d52f951402daa68b108d7129f4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Dictionary.meta
vendored
Normal file
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Dictionary.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a444c902ff594bbaa3a3fb394e7f53a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
||||
|
||||
namespace Fantasy.DataStructure.Dictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供对字典的扩展方法。
|
||||
/// </summary>
|
||||
public static class DictionaryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 尝试从字典中移除指定键,并返回相应的值。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TV">字典中值的类型。</typeparam>
|
||||
/// <param name="self">要操作的字典实例。</param>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
/// <param name="value">从字典中移除的值(如果成功移除)。</param>
|
||||
/// <returns>如果成功移除键值对,则为 true;否则为 false。</returns>
|
||||
public static bool TryRemove<T, TV>(this IDictionary<T, TV> self, T key, out TV value)
|
||||
{
|
||||
if (!self.TryGetValue(key, out value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
self.Remove(key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6975fd47ec81d474cbd1ba996d01e864
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
70
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Dictionary/DictionaryPool.cs
vendored
Normal file
70
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Dictionary/DictionaryPool.cs
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Pool;
|
||||
|
||||
namespace Fantasy.DataStructure.Dictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供一个可以使用对象池管理的字典类。
|
||||
/// </summary>
|
||||
/// <typeparam name="TM">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TN">字典中值的类型。</typeparam>
|
||||
public sealed class DictionaryPool<TM, TN> : Dictionary<TM, TN>, IDisposable, IPool where TM : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例占用的资源。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<DictionaryPool<TM, TN>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="DictionaryPool{TM, TN}"/> 实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的实例。</returns>
|
||||
public static DictionaryPool<TM, TN> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var dictionary = Pool<DictionaryPool<TM, TN>>.Rent();
|
||||
#else
|
||||
var dictionary = MultiThreadPool.Rent<DictionaryPool<TM, TN>>();
|
||||
#endif
|
||||
dictionary._isDispose = false;
|
||||
dictionary._isPool = true;
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 421f6528d2cff461dab994c2f358f24d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,289 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Pool;
|
||||
|
||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
#pragma warning disable CS8603 // Possible null reference return.
|
||||
|
||||
namespace Fantasy.DataStructure.Dictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供一个双向映射字典对象池类,用于双向键值对映射。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">字典中值的类型。</typeparam>
|
||||
public class DoubleMapDictionaryPool<TKey, TValue> : DoubleMapDictionary<TKey, TValue>, IDisposable, IPool where TKey : notnull where TValue : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="DoubleMapDictionaryPool{TKey, TValue}"/> 实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的实例。</returns>
|
||||
public static DoubleMapDictionaryPool<TKey, TValue> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var a = Pool<DoubleMapDictionaryPool<TKey, TValue>>.Rent();
|
||||
#else
|
||||
var a = MultiThreadPool.Rent<DoubleMapDictionaryPool<TKey, TValue>>();
|
||||
#endif
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例占用的资源。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<DoubleMapDictionaryPool<TKey, TValue>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 可以实现双向映射的字典类,用于将键和值进行双向映射。
|
||||
/// </summary>
|
||||
/// <typeparam name="TK">键的类型,不能为 null。</typeparam>
|
||||
/// <typeparam name="TV">值的类型,不能为 null。</typeparam>
|
||||
public class DoubleMapDictionary<TK, TV> where TK : notnull where TV : notnull
|
||||
{
|
||||
private readonly Dictionary<TK, TV> _kv = new Dictionary<TK, TV>();
|
||||
private readonly Dictionary<TV, TK> _vk = new Dictionary<TV, TK>();
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的空的 <see cref="DoubleMapDictionary{TK, TV}"/> 实例。
|
||||
/// </summary>
|
||||
public DoubleMapDictionary() { }
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的具有指定初始容量的 <see cref="DoubleMapDictionary{TK, TV}"/> 实例。
|
||||
/// </summary>
|
||||
/// <param name="capacity">初始容量。</param>
|
||||
public DoubleMapDictionary(int capacity)
|
||||
{
|
||||
_kv = new Dictionary<TK, TV>(capacity);
|
||||
_vk = new Dictionary<TV, TK>(capacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取包含字典中所有键的列表。
|
||||
/// </summary>
|
||||
public List<TK> Keys => new List<TK>(_kv.Keys);
|
||||
|
||||
/// <summary>
|
||||
/// 获取包含字典中所有值的列表。
|
||||
/// </summary>
|
||||
public List<TV> Values => new List<TV>(_vk.Keys);
|
||||
|
||||
/// <summary>
|
||||
/// 对字典中的每个键值对执行指定的操作。
|
||||
/// </summary>
|
||||
/// <param name="action">要执行的操作。</param>
|
||||
public void ForEach(Action<TK, TV> action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var keys = _kv.Keys;
|
||||
foreach (var key in keys)
|
||||
{
|
||||
action(key, _kv[key]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定的键值对添加到字典中。
|
||||
/// </summary>
|
||||
/// <param name="key">要添加的键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
public void Add(TK key, TV value)
|
||||
{
|
||||
if (key == null || value == null || _kv.ContainsKey(key) || _vk.ContainsKey(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_kv.Add(key, value);
|
||||
_vk.Add(value, key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据指定的键获取相应的值。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找值的键。</param>
|
||||
/// <returns>与指定键关联的值,如果找不到键,则返回默认值。</returns>
|
||||
public TV GetValueByKey(TK key)
|
||||
{
|
||||
if (key != null && _kv.ContainsKey(key))
|
||||
{
|
||||
return _kv[key];
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试根据指定的键获取相应的值。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找值的键。</param>
|
||||
/// <param name="value">如果找到,则为与指定键关联的值;否则为值的默认值。</param>
|
||||
/// <returns>如果找到键,则为 true;否则为 false。</returns>
|
||||
public bool TryGetValueByKey(TK key, out TV value)
|
||||
{
|
||||
var result = key != null && _kv.ContainsKey(key);
|
||||
|
||||
value = result ? _kv[key] : default;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据指定的值获取相应的键。
|
||||
/// </summary>
|
||||
/// <param name="value">要查找键的值。</param>
|
||||
/// <returns>与指定值关联的键,如果找不到值,则返回默认键。</returns>
|
||||
public TK GetKeyByValue(TV value)
|
||||
{
|
||||
if (value != null && _vk.ContainsKey(value))
|
||||
{
|
||||
return _vk[value];
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试根据指定的值获取相应的键。
|
||||
/// </summary>
|
||||
/// <param name="value">要查找键的值。</param>
|
||||
/// <param name="key">如果找到,则为与指定值关联的键;否则为键的默认值。</param>
|
||||
/// <returns>如果找到值,则为 true;否则为 false。</returns>
|
||||
public bool TryGetKeyByValue(TV value, out TK key)
|
||||
{
|
||||
var result = value != null && _vk.ContainsKey(value);
|
||||
|
||||
key = result ? _vk[value] : default;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据指定的键移除键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
public void RemoveByKey(TK key)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_kv.TryGetValue(key, out var value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_kv.Remove(key);
|
||||
_vk.Remove(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据指定的值移除键值对。
|
||||
/// </summary>
|
||||
/// <param name="value">要移除的值。</param>
|
||||
public void RemoveByValue(TV value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_vk.TryGetValue(value, out var key))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_kv.Remove(key);
|
||||
_vk.Remove(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空字典中的所有键值对。
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
_kv.Clear();
|
||||
_vk.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断字典是否包含指定的键。
|
||||
/// </summary>
|
||||
/// <param name="key">要检查的键。</param>
|
||||
/// <returns>如果字典包含指定的键,则为 true;否则为 false。</returns>
|
||||
public bool ContainsKey(TK key)
|
||||
{
|
||||
return key != null && _kv.ContainsKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断字典是否包含指定的值。
|
||||
/// </summary>
|
||||
/// <param name="value">要检查的值。</param>
|
||||
/// <returns>如果字典包含指定的值,则为 true;否则为 false。</returns>
|
||||
public bool ContainsValue(TV value)
|
||||
{
|
||||
return value != null && _vk.ContainsKey(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断字典是否包含指定的键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">要检查的键。</param>
|
||||
/// <param name="value">要检查的值。</param>
|
||||
/// <returns>如果字典包含指定的键值对,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TK key, TV value)
|
||||
{
|
||||
if (key == null || value == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _kv.ContainsKey(key) && _vk.ContainsKey(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a23375ac49ac64827a5d2e6ae476bbab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Pool;
|
||||
|
||||
namespace Fantasy.DataStructure.Dictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供一个带资源释放功能的实体字典类,支持使用对象池管理。
|
||||
/// </summary>
|
||||
/// <typeparam name="TM">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TN">字典中值的类型,必须实现 IDisposable 接口。</typeparam>
|
||||
public sealed class EntityDictionary<TM, TN> : Dictionary<TM, TN>, IDisposable, IPool where TN : IDisposable where TM : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="EntityDictionary{TM, TN}"/> 实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的实例。</returns>
|
||||
public static EntityDictionary<TM, TN> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var entityDictionary = Pool<EntityDictionary<TM, TN>>.Rent();
|
||||
#else
|
||||
var entityDictionary = MultiThreadPool.Rent<EntityDictionary<TM, TN>>();
|
||||
#endif
|
||||
entityDictionary._isDispose = false;
|
||||
entityDictionary._isPool = true;
|
||||
return entityDictionary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空字典中的所有键值对,并释放值的资源。
|
||||
/// </summary>
|
||||
public new void Clear()
|
||||
{
|
||||
foreach (var keyValuePair in this)
|
||||
{
|
||||
keyValuePair.Value.Dispose();
|
||||
}
|
||||
|
||||
base.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空字典中的所有键值对,但不释放值的资源。
|
||||
/// </summary>
|
||||
public void ClearNotDispose()
|
||||
{
|
||||
base.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例占用的资源。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<EntityDictionary<TM, TN>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30708df4a385e4aee83551b3371240fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,247 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Fantasy.Pool;
|
||||
|
||||
#pragma warning disable CS8603
|
||||
#pragma warning disable CS8601
|
||||
|
||||
namespace Fantasy.DataStructure.Dictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// 一对多映射关系的字典对象池。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">外部字典中的键类型。</typeparam>
|
||||
/// <typeparam name="TValueKey">内部字典中的键类型。</typeparam>
|
||||
/// <typeparam name="TValue">内部字典中的值类型。</typeparam>
|
||||
public class OneToManyDictionaryPool<TKey, TValueKey, TValue> : OneToManyDictionary<TKey, TValueKey, TValue>, IDisposable, IPool where TKey : notnull where TValueKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="OneToManyDictionaryPool{TKey, TValueKey, TValue}"/> 的实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的 OneToManyDictionaryPool 实例。</returns>
|
||||
public static OneToManyDictionaryPool<TKey, TValueKey, TValue> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var a = Pool<OneToManyDictionaryPool<TKey, TValueKey, TValue>>.Rent();
|
||||
#else
|
||||
var a = MultiThreadPool.Rent<OneToManyDictionaryPool<TKey, TValueKey, TValue>>();
|
||||
#endif
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放当前实例及其资源。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<OneToManyDictionaryPool<TKey, TValueKey, TValue>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 一对多映射关系的字典。每个键都对应一个内部字典,该内部字典将键值映射到相应的值。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">外部字典中的键类型。</typeparam>
|
||||
/// <typeparam name="TValueKey">内部字典中的键类型。</typeparam>
|
||||
/// <typeparam name="TValue">内部字典中的值类型。</typeparam>
|
||||
public class OneToManyDictionary<TKey, TValueKey, TValue> : Dictionary<TKey, Dictionary<TValueKey, TValue>>
|
||||
where TKey : notnull where TValueKey : notnull
|
||||
{
|
||||
private readonly Queue<Dictionary<TValueKey, TValue>> _queue = new Queue<Dictionary<TValueKey, TValue>>();
|
||||
private readonly int _recyclingLimit = 120;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="OneToManyDictionary{TKey, TValueKey, TValue}"/> 实例。
|
||||
/// </summary>
|
||||
public OneToManyDictionary() { }
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="OneToManyDictionary{TKey, TValueKey, TValue}"/> 实例,并指定最大缓存数量。
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public OneToManyDictionary(int recyclingLimit = 0)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否包含指定的键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">外部字典中的键。</param>
|
||||
/// <param name="valueKey">内部字典中的键。</param>
|
||||
/// <returns>如果包含指定的键值对,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TKey key, TValueKey valueKey)
|
||||
{
|
||||
TryGetValue(key, out var dic);
|
||||
|
||||
return dic != null && dic.ContainsKey(valueKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试获取指定键值对的值。
|
||||
/// </summary>
|
||||
/// <param name="key">外部字典中的键。</param>
|
||||
/// <param name="valueKey">内部字典中的键。</param>
|
||||
/// <param name="value">获取的值,如果操作成功,则为值;否则为默认值。</param>
|
||||
/// <returns>如果操作成功,则为 true;否则为 false。</returns>
|
||||
public bool TryGetValue(TKey key, TValueKey valueKey, out TValue value)
|
||||
{
|
||||
value = default;
|
||||
return TryGetValue(key, out var dic) && dic.TryGetValue(valueKey, out value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键的第一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要获取第一个值的键。</param>
|
||||
public TValue First(TKey key)
|
||||
{
|
||||
return !TryGetValue(key, out var dic) ? default : dic.First().Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向字典中添加指定的键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">要添加键值对的键。</param>
|
||||
/// <param name="valueKey">要添加键值对的内部字典键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
public void Add(TKey key, TValueKey valueKey, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var dic))
|
||||
{
|
||||
dic = Fetch();
|
||||
dic[valueKey] = value;
|
||||
// dic.Add(valueKey, value);
|
||||
Add(key, dic);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
dic[valueKey] = value;
|
||||
// dic.Add(valueKey, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定的键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除键值对的键。</param>
|
||||
/// <param name="valueKey">要移除键值对的内部字典键。</param>
|
||||
/// <returns>如果成功移除键值对,则为 true;否则为 false。</returns>
|
||||
public bool Remove(TKey key, TValueKey valueKey)
|
||||
{
|
||||
if (!TryGetValue(key, out var dic)) return false;
|
||||
|
||||
var result = dic.Remove(valueKey);
|
||||
|
||||
if (dic.Count == 0) RemoveKey(key);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定的键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除键值对的键。</param>
|
||||
/// <param name="valueKey">要移除键值对的内部字典键。</param>
|
||||
/// <param name="value">如果成功移除键值对,则为移除的值;否则为默认值。</param>
|
||||
/// <returns>如果成功移除键值对,则为 true;否则为 false。</returns>
|
||||
public bool Remove(TKey key, TValueKey valueKey, out TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var dic))
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var result = dic.TryGetValue(valueKey, out value);
|
||||
|
||||
if (result) dic.Remove(valueKey);
|
||||
|
||||
if (dic.Count == 0) RemoveKey(key);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除字典中的指定键及其相关的所有键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var dic)) return;
|
||||
|
||||
Remove(key);
|
||||
Recycle(dic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从对象池中获取一个内部字典实例,如果池中没有,则创建一个新实例。
|
||||
/// </summary>
|
||||
/// <returns>获取的内部字典实例。</returns>
|
||||
private Dictionary<TValueKey, TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new Dictionary<TValueKey, TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将不再使用的内部字典实例放回对象池中,以便后续重用。
|
||||
/// </summary>
|
||||
/// <param name="dic">要放回对象池的内部字典实例。</param>
|
||||
private void Recycle(Dictionary<TValueKey, TValue> dic)
|
||||
{
|
||||
dic.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(dic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空字典中的所有键值对,并将不再使用的内部字典实例放回对象池中。
|
||||
/// </summary>
|
||||
public new void Clear()
|
||||
{
|
||||
foreach (var keyValuePair in this) Recycle(keyValuePair.Value);
|
||||
|
||||
base.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4cd83a6bbc1914aa4bf1f94f1cba1c47
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,250 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Pool;
|
||||
|
||||
#pragma warning disable CS8601
|
||||
|
||||
namespace Fantasy.DataStructure.Dictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// 一对多映射关系的排序字典对象池。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">外部字典中的键类型。</typeparam>
|
||||
/// <typeparam name="TSortedKey">内部字典中的排序键类型。</typeparam>
|
||||
/// <typeparam name="TValue">内部字典中的值类型。</typeparam>
|
||||
public class OneToManySortedDictionaryPool<TKey, TSortedKey, TValue> : OneToManySortedDictionary<TKey, TSortedKey, TValue>, IDisposable, IPool where TKey : notnull where TSortedKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="OneToManySortedDictionaryPool{TKey, TSortedKey, TValue}"/> 的实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的 OneToManySortedDictionaryPool 实例。</returns>
|
||||
public static OneToManySortedDictionaryPool<TKey, TSortedKey, TValue> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var a = Pool<OneToManySortedDictionaryPool<TKey, TSortedKey, TValue>>.Rent();
|
||||
#else
|
||||
var a = MultiThreadPool.Rent<OneToManySortedDictionaryPool<TKey, TSortedKey, TValue>>();
|
||||
#endif
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放当前实例及其资源。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<OneToManySortedDictionaryPool<TKey, TSortedKey, TValue>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 一对多映射关系的排序字典。每个外部键映射到一个内部排序字典,该内部排序字典将排序键映射到相应的值。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">外部字典中的键类型。</typeparam>
|
||||
/// <typeparam name="TSortedKey">内部字典中的排序键类型。</typeparam>
|
||||
/// <typeparam name="TValue">内部字典中的值类型。</typeparam>
|
||||
public class
|
||||
OneToManySortedDictionary<TKey, TSortedKey, TValue> : Dictionary<TKey, SortedDictionary<TSortedKey, TValue>>
|
||||
where TSortedKey : notnull where TKey : notnull
|
||||
{
|
||||
/// 缓存队列的回收限制
|
||||
private readonly int _recyclingLimit = 120;
|
||||
/// 缓存队列,用于存储已回收的内部排序字典
|
||||
private readonly Queue<SortedDictionary<TSortedKey, TValue>> _queue = new Queue<SortedDictionary<TSortedKey, TValue>>();
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="OneToManySortedDictionary{TKey, TSortedKey, TValue}"/> 实例。
|
||||
/// </summary>
|
||||
protected OneToManySortedDictionary() { }
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="OneToManySortedDictionary{TKey, TSortedKey, TValue}"/> 实例。设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public OneToManySortedDictionary(int recyclingLimit)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查字典是否包含指定的外部键。
|
||||
/// </summary>
|
||||
/// <param name="key">要检查的外部键。</param>
|
||||
/// <returns>如果字典包含指定的外部键,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TKey key)
|
||||
{
|
||||
return this.ContainsKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查字典是否包含指定的外部键和排序键。
|
||||
/// </summary>
|
||||
/// <param name="key">要检查的外部键。</param>
|
||||
/// <param name="sortedKey">要检查的排序键。</param>
|
||||
/// <returns>如果字典包含指定的外部键和排序键,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TKey key, TSortedKey sortedKey)
|
||||
{
|
||||
return TryGetValue(key, out var dic) && dic.ContainsKey(sortedKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试从字典中获取指定外部键对应的内部排序字典。
|
||||
/// </summary>
|
||||
/// <param name="key">要获取内部排序字典的外部键。</param>
|
||||
/// <param name="dic">获取到的内部排序字典,如果找不到则为 null。</param>
|
||||
/// <returns>如果找到内部排序字典,则为 true;否则为 false。</returns>
|
||||
public new bool TryGetValue(TKey key, out SortedDictionary<TSortedKey, TValue> dic)
|
||||
{
|
||||
return base.TryGetValue(key, out dic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试从字典中获取指定外部键和排序键对应的值。
|
||||
/// </summary>
|
||||
/// <param name="key">要获取值的外部键。</param>
|
||||
/// <param name="sortedKey">要获取值的排序键。</param>
|
||||
/// <param name="value">获取到的值,如果找不到则为 default。</param>
|
||||
/// <returns>如果找到值,则为 true;否则为 false。</returns>
|
||||
public bool TryGetValueBySortedKey(TKey key, TSortedKey sortedKey, out TValue value)
|
||||
{
|
||||
if (base.TryGetValue(key, out var dic))
|
||||
{
|
||||
return dic.TryGetValue(sortedKey, out value);
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向字典中添加一个值,关联到指定的外部键和排序键。
|
||||
/// </summary>
|
||||
/// <param name="key">要关联值的外部键。</param>
|
||||
/// <param name="sortedKey">要关联值的排序键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
public void Add(TKey key, TSortedKey sortedKey, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var dic))
|
||||
{
|
||||
dic = Fetch();
|
||||
dic.Add(sortedKey, value);
|
||||
Add(key, dic);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
dic.Add(sortedKey, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定外部键和排序键关联的值。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除值的外部键。</param>
|
||||
/// <param name="sortedKey">要移除值的排序键。</param>
|
||||
/// <returns>如果成功移除值,则为 true;否则为 false。</returns>
|
||||
public bool RemoveSortedKey(TKey key, TSortedKey sortedKey)
|
||||
{
|
||||
if (!TryGetValue(key, out var dic))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var isRemove = dic.Remove(sortedKey);
|
||||
|
||||
if (dic.Count == 0)
|
||||
{
|
||||
isRemove = RemoveKey(key);
|
||||
}
|
||||
|
||||
return isRemove;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定外部键及其关联的所有值。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的外部键。</param>
|
||||
/// <returns>如果成功移除外部键及其关联的所有值,则为 true;否则为 false。</returns>
|
||||
public bool RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Remove(key);
|
||||
Recycle(list);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存队列中获取一个内部排序字典。
|
||||
/// </summary>
|
||||
/// <returns>一个内部排序字典。</returns>
|
||||
private SortedDictionary<TSortedKey, TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new SortedDictionary<TSortedKey, TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回收一个内部排序字典到缓存队列。
|
||||
/// </summary>
|
||||
/// <param name="dic">要回收的内部排序字典。</param>
|
||||
private void Recycle(SortedDictionary<TSortedKey, TValue> dic)
|
||||
{
|
||||
dic.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_queue.Enqueue(dic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空字典以及内部排序字典缓存队列,释放所有资源。
|
||||
/// </summary>
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ece24c9ac70d549ea953523c0e5f7337
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
70
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Dictionary/ReuseDictionary.cs
vendored
Normal file
70
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/Dictionary/ReuseDictionary.cs
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Pool;
|
||||
|
||||
namespace Fantasy.DataStructure.Dictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供一个可以重用的字典类,支持使用对象池管理。
|
||||
/// </summary>
|
||||
/// <typeparam name="TM">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TN">字典中值的类型。</typeparam>
|
||||
public sealed class ReuseDictionary<TM, TN> : Dictionary<TM, TN>, IDisposable, IPool where TM : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="ReuseDictionary{TM, TN}"/> 实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的实例。</returns>
|
||||
public static ReuseDictionary<TM, TN> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var entityDictionary = Pool<ReuseDictionary<TM, TN>>.Rent();
|
||||
#else
|
||||
var entityDictionary = MultiThreadPool.Rent<ReuseDictionary<TM, TN>>();
|
||||
#endif
|
||||
entityDictionary._isDispose = false;
|
||||
entityDictionary._isPool = true;
|
||||
return entityDictionary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例占用的资源。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<ReuseDictionary<TM, TN>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 882f28b6907a3465dbf9df04603f74b5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.Pool;
|
||||
|
||||
namespace Fantasy.DataStructure.Dictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供一个可以使用对象池管理的排序字典类。
|
||||
/// </summary>
|
||||
/// <typeparam name="TM"></typeparam>
|
||||
/// <typeparam name="TN"></typeparam>
|
||||
public sealed class SortedDictionaryPool<TM, TN> : SortedDictionary<TM, TN>, IDisposable, IPool where TM : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例占用的资源。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<SortedDictionaryPool<TM, TN>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="SortedDictionaryPool{TM, TN}"/> 实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的实例。</returns>
|
||||
public static SortedDictionaryPool<TM, TN> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var dictionary = Pool<SortedDictionaryPool<TM, TN>>.Rent();
|
||||
#else
|
||||
var dictionary = MultiThreadPool.Rent<SortedDictionaryPool<TM, TN>>();
|
||||
#endif
|
||||
dictionary._isDispose = false;
|
||||
dictionary._isPool = true;
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 060121583adb64f03973db7e623b91de
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/NativeCollections.meta
vendored
Normal file
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/NativeCollections.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e0c05d3a90bc4c5d88144a87507f06d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/PriorityQueue.meta
vendored
Normal file
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/PriorityQueue.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2af7e2648850348b591adabb5ba44532
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,121 @@
|
||||
// ReSharper disable SwapViaDeconstruction
|
||||
// ReSharper disable UseIndexFromEndExpression
|
||||
// ReSharper disable ConvertToPrimaryConstructor
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
||||
namespace Fantasy.DataStructure.PriorityQueue
|
||||
{
|
||||
/// <summary>
|
||||
/// 优先队列
|
||||
/// </summary>
|
||||
/// <typeparam name="TElement">节点数据</typeparam>
|
||||
/// <typeparam name="TPriority">排序的类型、</typeparam>
|
||||
public sealed class PriorityQueue<TElement, TPriority> where TPriority : IComparable<TPriority>
|
||||
{
|
||||
private readonly List<PriorityQueueItem<TElement, TPriority>> _heap;
|
||||
|
||||
public PriorityQueue(int initialCapacity = 16)
|
||||
{
|
||||
_heap = new List<PriorityQueueItem<TElement, TPriority>>(initialCapacity);
|
||||
}
|
||||
|
||||
public int Count => _heap.Count;
|
||||
|
||||
public void Enqueue(TElement element, TPriority priority)
|
||||
{
|
||||
_heap.Add(new PriorityQueueItem<TElement, TPriority>(element, priority));
|
||||
HeapifyUp(_heap.Count - 1);
|
||||
}
|
||||
|
||||
public TElement Dequeue()
|
||||
{
|
||||
if (_heap.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("The queue is empty.");
|
||||
}
|
||||
|
||||
var item = _heap[0];
|
||||
_heap[0] = _heap[_heap.Count - 1];
|
||||
_heap.RemoveAt(_heap.Count - 1);
|
||||
HeapifyDown(0);
|
||||
return item.Element;
|
||||
}
|
||||
|
||||
public bool TryDequeue(out TElement element)
|
||||
{
|
||||
if (_heap.Count == 0)
|
||||
{
|
||||
element = default(TElement);
|
||||
return false;
|
||||
}
|
||||
|
||||
element = Dequeue();
|
||||
return true;
|
||||
}
|
||||
|
||||
public TElement Peek()
|
||||
{
|
||||
if (_heap.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("The queue is empty.");
|
||||
}
|
||||
return _heap[0].Element;
|
||||
}
|
||||
|
||||
// ReSharper disable once IdentifierTypo
|
||||
private void HeapifyUp(int index)
|
||||
{
|
||||
while (index > 0)
|
||||
{
|
||||
var parentIndex = (index - 1) / 2;
|
||||
if (_heap[index].Priority.CompareTo(_heap[parentIndex].Priority) >= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
Swap(index, parentIndex);
|
||||
index = parentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once IdentifierTypo
|
||||
private void HeapifyDown(int index)
|
||||
{
|
||||
var lastIndex = _heap.Count - 1;
|
||||
while (true)
|
||||
{
|
||||
var smallestIndex = index;
|
||||
var leftChildIndex = 2 * index + 1;
|
||||
var rightChildIndex = 2 * index + 2;
|
||||
|
||||
if (leftChildIndex <= lastIndex && _heap[leftChildIndex].Priority.CompareTo(_heap[smallestIndex].Priority) < 0)
|
||||
{
|
||||
smallestIndex = leftChildIndex;
|
||||
}
|
||||
|
||||
if (rightChildIndex <= lastIndex && _heap[rightChildIndex].Priority.CompareTo(_heap[smallestIndex].Priority) < 0)
|
||||
{
|
||||
smallestIndex = rightChildIndex;
|
||||
}
|
||||
|
||||
if (smallestIndex == index)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Swap(index, smallestIndex);
|
||||
index = smallestIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private void Swap(int index1, int index2)
|
||||
{
|
||||
var temp = _heap[index1];
|
||||
_heap[index1] = _heap[index2];
|
||||
_heap[index2] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0fb73a1acb1843ad86127be8503ed92
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
// ReSharper disable ConvertToPrimaryConstructor
|
||||
// ReSharper disable SwapViaDeconstruction
|
||||
// ReSharper disable InconsistentNaming
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
namespace Fantasy.DataStructure.PriorityQueue
|
||||
{
|
||||
public struct PriorityQueueItemUint<T>
|
||||
{
|
||||
public T Element { get; set; }
|
||||
public uint Priority { get; set; }
|
||||
|
||||
public PriorityQueueItemUint(T element, uint priority)
|
||||
{
|
||||
Element = element;
|
||||
Priority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
public struct PriorityQueueItem<T, T1>
|
||||
{
|
||||
public T Element { get; }
|
||||
public T1 Priority { get; }
|
||||
|
||||
public PriorityQueueItem(T element, T1 priority)
|
||||
{
|
||||
Element = element;
|
||||
Priority = priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79c4ab0531d874383b52d8b7082cf3a8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,116 @@
|
||||
// ReSharper disable SwapViaDeconstruction
|
||||
// ReSharper disable UseIndexFromEndExpression
|
||||
// ReSharper disable ConvertToPrimaryConstructor
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
||||
namespace Fantasy.DataStructure.PriorityQueue
|
||||
{
|
||||
public sealed class PriorityQueue<T> where T : IComparable<T>
|
||||
{
|
||||
private readonly List<T> _heap;
|
||||
|
||||
public PriorityQueue(int initialCapacity = 16)
|
||||
{
|
||||
_heap = new List<T>(initialCapacity);
|
||||
}
|
||||
|
||||
public int Count => _heap.Count;
|
||||
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
_heap.Add(item);
|
||||
HeapifyUp(_heap.Count - 1);
|
||||
}
|
||||
|
||||
public T Dequeue()
|
||||
{
|
||||
if (_heap.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("The queue is empty.");
|
||||
}
|
||||
|
||||
var item = _heap[0];
|
||||
var heapCount = _heap.Count - 1;
|
||||
_heap[0] = _heap[heapCount];
|
||||
_heap.RemoveAt(heapCount);
|
||||
HeapifyDown(0);
|
||||
return item;
|
||||
}
|
||||
|
||||
public bool TryDequeue(out T item)
|
||||
{
|
||||
if (_heap.Count == 0)
|
||||
{
|
||||
item = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
item = Dequeue();
|
||||
return true;
|
||||
}
|
||||
|
||||
public T Peek()
|
||||
{
|
||||
if (_heap.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("The queue is empty.");
|
||||
}
|
||||
return _heap[0];
|
||||
}
|
||||
|
||||
// ReSharper disable once IdentifierTypo
|
||||
private void HeapifyUp(int index)
|
||||
{
|
||||
while (index > 0)
|
||||
{
|
||||
var parentIndex = (index - 1) / 2;
|
||||
if (_heap[index].CompareTo(_heap[parentIndex]) >= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
Swap(index, parentIndex);
|
||||
index = parentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once IdentifierTypo
|
||||
private void HeapifyDown(int index)
|
||||
{
|
||||
var lastIndex = _heap.Count - 1;
|
||||
while (true)
|
||||
{
|
||||
var smallestIndex = index;
|
||||
var leftChildIndex = 2 * index + 1;
|
||||
var rightChildIndex = 2 * index + 2;
|
||||
|
||||
if (leftChildIndex <= lastIndex && _heap[leftChildIndex].CompareTo(_heap[smallestIndex]) < 0)
|
||||
{
|
||||
smallestIndex = leftChildIndex;
|
||||
}
|
||||
|
||||
if (rightChildIndex <= lastIndex && _heap[rightChildIndex].CompareTo(_heap[smallestIndex]) < 0)
|
||||
{
|
||||
smallestIndex = rightChildIndex;
|
||||
}
|
||||
|
||||
if (smallestIndex == index)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Swap(index, smallestIndex);
|
||||
index = smallestIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private void Swap(int index1, int index2)
|
||||
{
|
||||
var temp = _heap[index1];
|
||||
_heap[index1] = _heap[index2];
|
||||
_heap[index2] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c604a0410d104d68a5d1718a570e5c0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/SkipTable.meta
vendored
Normal file
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/SkipTable.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68c20f8b3fb7f461f9b2f22aa39b4c2e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
190
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/SkipTable/SkipTable.cs
vendored
Normal file
190
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/SkipTable/SkipTable.cs
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
namespace Fantasy.DataStructure.SkipTable
|
||||
{
|
||||
/// <summary>
|
||||
/// 跳表数据结构(升序版)
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">跳表中存储的值的类型。</typeparam>
|
||||
public class SkipTable<TValue> : SkipTableBase<TValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建一个新的跳表实例。
|
||||
/// </summary>
|
||||
/// <param name="maxLayer">跳表的最大层数。</param>
|
||||
public SkipTable(int maxLayer = 8) : base(maxLayer) { }
|
||||
|
||||
/// <summary>
|
||||
/// 向跳表中添加一个新节点。
|
||||
/// </summary>
|
||||
/// <param name="sortKey">节点的主排序键。</param>
|
||||
/// <param name="viceKey">节点的副排序键。</param>
|
||||
/// <param name="key">节点的唯一键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
public override void Add(long sortKey, long viceKey, long key, TValue value)
|
||||
{
|
||||
var rLevel = 1;
|
||||
|
||||
while (rLevel <= MaxLayer && Random.Next(3) == 0)
|
||||
{
|
||||
++rLevel;
|
||||
}
|
||||
|
||||
SkipTableNode<TValue> cur = TopHeader, last = null;
|
||||
|
||||
for (var layer = MaxLayer; layer >= 1; --layer)
|
||||
{
|
||||
// 节点有next节点,且 (next主键 < 插入主键) 或 (next主键 == 插入主键 且 next副键 < 插入副键)
|
||||
while (cur.Right != null && ((cur.Right.SortKey < sortKey) ||
|
||||
(cur.Right.SortKey == sortKey && cur.Right.ViceKey < viceKey)))
|
||||
{
|
||||
cur = cur.Right;
|
||||
}
|
||||
|
||||
if (layer <= rLevel)
|
||||
{
|
||||
var currentRight = cur.Right;
|
||||
|
||||
// 在当前层插入新节点
|
||||
cur.Right = new SkipTableNode<TValue>(sortKey, viceKey, key, value, layer == 1 ? cur.Index + 1 : 0, cur, cur.Right, null);
|
||||
|
||||
if (currentRight != null)
|
||||
{
|
||||
currentRight.Left = cur.Right;
|
||||
}
|
||||
|
||||
if (last != null)
|
||||
{
|
||||
last.Down = cur.Right;
|
||||
}
|
||||
|
||||
if (layer == 1)
|
||||
{
|
||||
// 更新索引信息
|
||||
cur.Right.Index = cur.Index + 1;
|
||||
Node.Add(key, cur.Right);
|
||||
|
||||
SkipTableNode<TValue> v = cur.Right.Right;
|
||||
|
||||
while (v != null)
|
||||
{
|
||||
v.Index++;
|
||||
v = v.Right;
|
||||
}
|
||||
}
|
||||
|
||||
last = cur.Right;
|
||||
}
|
||||
|
||||
cur = cur.Down;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从跳表中移除一个节点。
|
||||
/// </summary>
|
||||
/// <param name="sortKey">节点的主排序键。</param>
|
||||
/// <param name="viceKey">节点的副排序键。</param>
|
||||
/// <param name="key">节点的唯一键。</param>
|
||||
/// <param name="value">被移除的节点的值。</param>
|
||||
/// <returns>如果成功移除节点,则为 true;否则为 false。</returns>
|
||||
public override bool Remove(long sortKey, long viceKey, long key, out TValue value)
|
||||
{
|
||||
value = default;
|
||||
var seen = false;
|
||||
var cur = TopHeader;
|
||||
|
||||
for (var layer = MaxLayer; layer >= 1; --layer)
|
||||
{
|
||||
// 先按照主键查找 再 按副键查找
|
||||
while (cur.Right != null && cur.Right.SortKey < sortKey && cur.Right.Key != key) cur = cur.Right;
|
||||
while (cur.Right != null && (cur.Right.SortKey == sortKey && cur.Right.ViceKey <= viceKey) &&
|
||||
cur.Right.Key != key) cur = cur.Right;
|
||||
|
||||
var isFind = false;
|
||||
var currentCur = cur;
|
||||
SkipTableNode<TValue> removeCur = null;
|
||||
// 如果当前不是要删除的节点、但主键和副键都一样、需要特殊处理下。
|
||||
if (cur.Right != null && cur.Right.Key == key)
|
||||
{
|
||||
isFind = true;
|
||||
removeCur = cur.Right;
|
||||
currentCur = cur;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 先向左查找下
|
||||
var currentNode = cur.Left;
|
||||
while (currentNode != null && currentNode.SortKey == sortKey && currentNode.ViceKey == viceKey)
|
||||
{
|
||||
if (currentNode.Key == key)
|
||||
{
|
||||
isFind = true;
|
||||
removeCur = currentNode;
|
||||
currentCur = currentNode.Left;
|
||||
break;
|
||||
}
|
||||
|
||||
currentNode = currentNode.Left;
|
||||
}
|
||||
|
||||
// 再向右查找下
|
||||
if (!isFind)
|
||||
{
|
||||
currentNode = cur.Right;
|
||||
while (currentNode != null && currentNode.SortKey == sortKey && currentNode.ViceKey == viceKey)
|
||||
{
|
||||
if (currentNode.Key == key)
|
||||
{
|
||||
isFind = true;
|
||||
removeCur = currentNode;
|
||||
currentCur = currentNode.Left;
|
||||
break;
|
||||
}
|
||||
|
||||
currentNode = currentNode.Right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isFind && currentCur != null)
|
||||
{
|
||||
value = removeCur.Value;
|
||||
currentCur.Right = removeCur.Right;
|
||||
|
||||
if (removeCur.Right != null)
|
||||
{
|
||||
removeCur.Right.Left = currentCur;
|
||||
removeCur.Right = null;
|
||||
}
|
||||
|
||||
removeCur.Left = null;
|
||||
removeCur.Down = null;
|
||||
removeCur.Value = default;
|
||||
|
||||
if (layer == 1)
|
||||
{
|
||||
var tempCur = currentCur.Right;
|
||||
while (tempCur != null)
|
||||
{
|
||||
tempCur.Index--;
|
||||
tempCur = tempCur.Right;
|
||||
}
|
||||
|
||||
Node.Remove(removeCur.Key);
|
||||
}
|
||||
|
||||
seen = true;
|
||||
}
|
||||
|
||||
cur = cur.Down;
|
||||
}
|
||||
|
||||
return seen;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/SkipTable/SkipTable.cs.meta
vendored
Normal file
11
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/SkipTable/SkipTable.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45fbcdc219ef843aaa31594eb4e76a5b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
282
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/SkipTable/SkipTableBase.cs
vendored
Normal file
282
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/SkipTable/SkipTableBase.cs
vendored
Normal file
@@ -0,0 +1,282 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Fantasy.DataStructure.Collection;
|
||||
|
||||
#pragma warning disable CS8601
|
||||
#pragma warning disable CS8603
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8604
|
||||
|
||||
namespace Fantasy.DataStructure.SkipTable
|
||||
{
|
||||
/// <summary>
|
||||
/// 抽象的跳表基类,提供跳表的基本功能和操作。
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">跳表中存储的值的类型。</typeparam>
|
||||
public abstract class SkipTableBase<TValue> : IEnumerable<SkipTableNode<TValue>>
|
||||
{
|
||||
/// <summary>
|
||||
/// 跳表的最大层数
|
||||
/// </summary>
|
||||
public readonly int MaxLayer;
|
||||
/// <summary>
|
||||
/// 跳表的顶部头节点
|
||||
/// </summary>
|
||||
public readonly SkipTableNode<TValue> TopHeader;
|
||||
/// <summary>
|
||||
/// 跳表的底部头节点
|
||||
/// </summary>
|
||||
public SkipTableNode<TValue> BottomHeader;
|
||||
/// <summary>
|
||||
/// 跳表中节点的数量,使用了 Node 字典的计数
|
||||
/// </summary>
|
||||
public int Count => Node.Count;
|
||||
/// <summary>
|
||||
/// 用于生成随机数的随机数生成器
|
||||
/// </summary>
|
||||
protected readonly Random Random = new Random();
|
||||
/// <summary>
|
||||
/// 存储跳表节点的字典
|
||||
/// </summary>
|
||||
protected readonly Dictionary<long, SkipTableNode<TValue>> Node = new();
|
||||
/// <summary>
|
||||
/// 用于辅助反向查找的栈
|
||||
/// </summary>
|
||||
protected readonly Stack<SkipTableNode<TValue>> AntiFindStack = new Stack<SkipTableNode<TValue>>();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化一个新的跳表实例。
|
||||
/// </summary>
|
||||
/// <param name="maxLayer">跳表的最大层数,默认为 8。</param>
|
||||
protected SkipTableBase(int maxLayer = 8)
|
||||
{
|
||||
MaxLayer = maxLayer;
|
||||
var cur = TopHeader = new SkipTableNode<TValue>(long.MinValue, 0, 0, default, 0, null, null, null);
|
||||
|
||||
for (var layer = MaxLayer - 1; layer >= 1; --layer)
|
||||
{
|
||||
cur.Down = new SkipTableNode<TValue>(long.MinValue, 0, 0, default, 0, null, null, null);
|
||||
cur = cur.Down;
|
||||
}
|
||||
|
||||
BottomHeader = cur;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键的节点的值,若不存在则返回默认值。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找的键。</param>
|
||||
public TValue this[long key] => !TryGetValueByKey(key, out TValue value) ? default : value;
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键的节点在跳表中的排名。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找的键。</param>
|
||||
/// <returns>节点的排名。</returns>
|
||||
public int GetRanking(long key)
|
||||
{
|
||||
if (!Node.TryGetValue(key, out var node))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return node.Index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键的反向排名,即在比该键更大的节点中的排名。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找的键。</param>
|
||||
/// <returns>反向排名。</returns>
|
||||
public int GetAntiRanking(long key)
|
||||
{
|
||||
var ranking = GetRanking(key);
|
||||
|
||||
if (ranking == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Count + 1 - ranking;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试通过键获取节点的值。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找的键。</param>
|
||||
/// <param name="value">获取到的节点的值,如果键不存在则为默认值。</param>
|
||||
/// <returns>是否成功获取节点的值。</returns>
|
||||
public bool TryGetValueByKey(long key, out TValue value)
|
||||
{
|
||||
if (!Node.TryGetValue(key, out var node))
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = node.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试通过键获取节点。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找的键。</param>
|
||||
/// <param name="node">获取到的节点,如果键不存在则为 <c>null</c>。</param>
|
||||
/// <returns>是否成功获取节点。</returns>
|
||||
public bool TryGetNodeByKey(long key, out SkipTableNode<TValue> node)
|
||||
{
|
||||
if (Node.TryGetValue(key, out node))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在跳表中查找节点,返回从起始位置到结束位置的节点列表。
|
||||
/// </summary>
|
||||
/// <param name="start">起始位置的排名。</param>
|
||||
/// <param name="end">结束位置的排名。</param>
|
||||
/// <param name="list">用于存储节点列表的 <see cref="ListPool{T}"/> 实例。</param>
|
||||
public void Find(int start, int end, ListPool<SkipTableNode<TValue>> list)
|
||||
{
|
||||
var cur = BottomHeader;
|
||||
var count = end - start;
|
||||
|
||||
for (var i = 0; i < start; i++)
|
||||
{
|
||||
cur = cur.Right;
|
||||
}
|
||||
|
||||
for (var i = 0; i <= count; i++)
|
||||
{
|
||||
if (cur == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
list.Add(cur);
|
||||
cur = cur.Right;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在跳表中进行反向查找节点,返回从结束位置到起始位置的节点列表。
|
||||
/// </summary>
|
||||
/// <param name="start">结束位置的排名。</param>
|
||||
/// <param name="end">起始位置的排名。</param>
|
||||
/// <param name="list">用于存储节点列表的 <see cref="ListPool{T}"/> 实例。</param>
|
||||
public void AntiFind(int start, int end, ListPool<SkipTableNode<TValue>> list)
|
||||
{
|
||||
var cur = BottomHeader;
|
||||
start = Count + 1 - start;
|
||||
end = start - end;
|
||||
|
||||
for (var i = 0; i < start; i++)
|
||||
{
|
||||
cur = cur.Right;
|
||||
|
||||
if (cur == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < end)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AntiFindStack.Push(cur);
|
||||
}
|
||||
|
||||
while (AntiFindStack.TryPop(out var node))
|
||||
{
|
||||
list.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取跳表中最后一个节点的值。
|
||||
/// </summary>
|
||||
/// <returns>最后一个节点的值。</returns>
|
||||
public TValue GetLastValue()
|
||||
{
|
||||
var cur = TopHeader;
|
||||
|
||||
while (cur.Right != null || cur.Down != null)
|
||||
{
|
||||
while (cur.Right != null)
|
||||
{
|
||||
cur = cur.Right;
|
||||
}
|
||||
|
||||
if (cur.Down != null)
|
||||
{
|
||||
cur = cur.Down;
|
||||
}
|
||||
}
|
||||
|
||||
return cur.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除跳表中指定键的节点。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的节点的键。</param>
|
||||
/// <returns>移除是否成功。</returns>
|
||||
public bool Remove(long key)
|
||||
{
|
||||
if (!Node.TryGetValue(key, out var node))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Remove(node.SortKey, node.ViceKey, key, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向跳表中添加节点。
|
||||
/// </summary>
|
||||
/// <param name="sortKey">节点的排序键。</param>
|
||||
/// <param name="viceKey">节点的副键。</param>
|
||||
/// <param name="key">节点的键。</param>
|
||||
/// <param name="value">节点的值。</param>
|
||||
public abstract void Add(long sortKey, long viceKey, long key, TValue value);
|
||||
|
||||
/// <summary>
|
||||
/// 从跳表中移除指定键的节点。
|
||||
/// </summary>
|
||||
/// <param name="sortKey">节点的排序键。</param>
|
||||
/// <param name="viceKey">节点的副键。</param>
|
||||
/// <param name="key">节点的键。</param>
|
||||
/// <param name="value">被移除的节点的值。</param>
|
||||
/// <returns>移除是否成功。</returns>
|
||||
public abstract bool Remove(long sortKey, long viceKey, long key, out TValue value);
|
||||
|
||||
/// <summary>
|
||||
/// 返回一个枚举器,用于遍历跳表中的节点。
|
||||
/// </summary>
|
||||
/// <returns>一个可用于遍历跳表节点的枚举器。</returns>
|
||||
public IEnumerator<SkipTableNode<TValue>> GetEnumerator()
|
||||
{
|
||||
var cur = BottomHeader.Right;
|
||||
while (cur != null)
|
||||
{
|
||||
yield return cur;
|
||||
cur = cur.Right;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回一个非泛型枚举器,用于遍历跳表中的节点。
|
||||
/// </summary>
|
||||
/// <returns>一个非泛型枚举器,可用于遍历跳表节点。</returns>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c70c703e84d842b9b488dd0798290d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
188
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/SkipTable/SkipTableDesc.cs
vendored
Normal file
188
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/SkipTable/SkipTableDesc.cs
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
|
||||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
||||
namespace Fantasy.DataStructure.SkipTable
|
||||
{
|
||||
/// <summary>
|
||||
/// 跳表降序版,用于存储降序排列的数据。
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">存储的值的类型。</typeparam>
|
||||
public class SkipTableDesc<TValue> : SkipTableBase<TValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化跳表降序版的新实例。
|
||||
/// </summary>
|
||||
/// <param name="maxLayer">跳表的最大层数,默认为 8。</param>
|
||||
public SkipTableDesc(int maxLayer = 8) : base(maxLayer) { }
|
||||
|
||||
/// <summary>
|
||||
/// 向跳表中添加一个节点,根据降序规则进行插入。
|
||||
/// </summary>
|
||||
/// <param name="sortKey">排序主键。</param>
|
||||
/// <param name="viceKey">副键。</param>
|
||||
/// <param name="key">键。</param>
|
||||
/// <param name="value">值。</param>
|
||||
public override void Add(long sortKey, long viceKey, long key, TValue value)
|
||||
{
|
||||
var rLevel = 1;
|
||||
|
||||
while (rLevel <= MaxLayer && Random.Next(3) == 0)
|
||||
{
|
||||
++rLevel;
|
||||
}
|
||||
|
||||
SkipTableNode<TValue> cur = TopHeader, last = null;
|
||||
|
||||
for (var layer = MaxLayer; layer >= 1; --layer)
|
||||
{
|
||||
// 节点有next节点,且 (next主键 > 插入主键) 或 (next主键 == 插入主键 且 next副键 > 插入副键)
|
||||
while (cur.Right != null && ((cur.Right.SortKey > sortKey) ||
|
||||
(cur.Right.SortKey == sortKey && cur.Right.ViceKey > viceKey)))
|
||||
{
|
||||
cur = cur.Right;
|
||||
}
|
||||
|
||||
if (layer <= rLevel)
|
||||
{
|
||||
var currentRight = cur.Right;
|
||||
cur.Right = new SkipTableNode<TValue>(sortKey, viceKey, key, value,
|
||||
layer == 1 ? cur.Index + 1 : 0, cur, cur.Right, null);
|
||||
|
||||
if (currentRight != null)
|
||||
{
|
||||
currentRight.Left = cur.Right;
|
||||
}
|
||||
|
||||
if (last != null)
|
||||
{
|
||||
last.Down = cur.Right;
|
||||
}
|
||||
|
||||
if (layer == 1)
|
||||
{
|
||||
cur.Right.Index = cur.Index + 1;
|
||||
Node.Add(key, cur.Right);
|
||||
|
||||
SkipTableNode<TValue> v = cur.Right.Right;
|
||||
|
||||
while (v != null)
|
||||
{
|
||||
v.Index++;
|
||||
v = v.Right;
|
||||
}
|
||||
}
|
||||
|
||||
last = cur.Right;
|
||||
}
|
||||
|
||||
cur = cur.Down;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从跳表中移除一个节点,根据降序规则进行移除。
|
||||
/// </summary>
|
||||
/// <param name="sortKey">排序主键。</param>
|
||||
/// <param name="viceKey">副键。</param>
|
||||
/// <param name="key">键。</param>
|
||||
/// <param name="value">移除的节点值。</param>
|
||||
/// <returns>如果成功移除节点,则返回 true,否则返回 false。</returns>
|
||||
public override bool Remove(long sortKey, long viceKey, long key, out TValue value)
|
||||
{
|
||||
value = default;
|
||||
var seen = false;
|
||||
var cur = TopHeader;
|
||||
|
||||
for (var layer = MaxLayer; layer >= 1; --layer)
|
||||
{
|
||||
// 先按照主键查找 再 按副键查找
|
||||
while (cur.Right != null && cur.Right.SortKey > sortKey && cur.Right.Key != key) cur = cur.Right;
|
||||
while (cur.Right != null && (cur.Right.SortKey == sortKey && cur.Right.ViceKey >= viceKey) &&
|
||||
cur.Right.Key != key) cur = cur.Right;
|
||||
|
||||
var isFind = false;
|
||||
var currentCur = cur;
|
||||
SkipTableNode<TValue> removeCur = null;
|
||||
// 如果当前不是要删除的节点、但主键和副键都一样、需要特殊处理下。
|
||||
if (cur.Right != null && cur.Right.Key == key)
|
||||
{
|
||||
isFind = true;
|
||||
removeCur = cur.Right;
|
||||
currentCur = cur;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 先向左查找下
|
||||
var currentNode = cur.Left;
|
||||
while (currentNode != null && currentNode.SortKey == sortKey && currentNode.ViceKey == viceKey)
|
||||
{
|
||||
if (currentNode.Key == key)
|
||||
{
|
||||
isFind = true;
|
||||
removeCur = currentNode;
|
||||
currentCur = currentNode.Left;
|
||||
break;
|
||||
}
|
||||
|
||||
currentNode = currentNode.Left;
|
||||
}
|
||||
|
||||
// 再向右查找下
|
||||
if (!isFind)
|
||||
{
|
||||
currentNode = cur.Right;
|
||||
while (currentNode != null && currentNode.SortKey == sortKey && currentNode.ViceKey == viceKey)
|
||||
{
|
||||
if (currentNode.Key == key)
|
||||
{
|
||||
isFind = true;
|
||||
removeCur = currentNode;
|
||||
currentCur = currentNode.Left;
|
||||
break;
|
||||
}
|
||||
|
||||
currentNode = currentNode.Right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isFind && currentCur != null)
|
||||
{
|
||||
value = removeCur.Value;
|
||||
currentCur.Right = removeCur.Right;
|
||||
|
||||
if (removeCur.Right != null)
|
||||
{
|
||||
removeCur.Right.Left = currentCur;
|
||||
removeCur.Right = null;
|
||||
}
|
||||
|
||||
removeCur.Left = null;
|
||||
removeCur.Down = null;
|
||||
removeCur.Value = default;
|
||||
|
||||
if (layer == 1)
|
||||
{
|
||||
var tempCur = currentCur.Right;
|
||||
while (tempCur != null)
|
||||
{
|
||||
tempCur.Index--;
|
||||
tempCur = tempCur.Right;
|
||||
}
|
||||
|
||||
Node.Remove(removeCur.Key);
|
||||
}
|
||||
|
||||
seen = true;
|
||||
}
|
||||
|
||||
cur = cur.Down;
|
||||
}
|
||||
|
||||
return seen;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae15364981a674c59a2e20d2b99f097b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
68
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/SkipTable/SkipTableNode.cs
vendored
Normal file
68
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/DataStructure/SkipTable/SkipTableNode.cs
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
namespace Fantasy.DataStructure.SkipTable
|
||||
{
|
||||
/// <summary>
|
||||
/// 跳跃表节点。
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">节点的值的类型。</typeparam>
|
||||
public class SkipTableNode<TValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// 节点在跳跃表中的索引。
|
||||
/// </summary>
|
||||
public int Index;
|
||||
/// <summary>
|
||||
/// 节点的主键。
|
||||
/// </summary>
|
||||
public long Key;
|
||||
/// <summary>
|
||||
/// 节点的排序键。
|
||||
/// </summary>
|
||||
public long SortKey;
|
||||
/// <summary>
|
||||
/// 节点的副键。
|
||||
/// </summary>
|
||||
public long ViceKey;
|
||||
/// <summary>
|
||||
/// 节点存储的值。
|
||||
/// </summary>
|
||||
public TValue Value;
|
||||
/// <summary>
|
||||
/// 指向左侧节点的引用。
|
||||
/// </summary>
|
||||
public SkipTableNode<TValue> Left;
|
||||
/// <summary>
|
||||
/// 指向右侧节点的引用。
|
||||
/// </summary>
|
||||
public SkipTableNode<TValue> Right;
|
||||
/// <summary>
|
||||
/// 指向下一层节点的引用。
|
||||
/// </summary>
|
||||
public SkipTableNode<TValue> Down;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化跳跃表节点的新实例。
|
||||
/// </summary>
|
||||
/// <param name="sortKey">节点的排序键。</param>
|
||||
/// <param name="viceKey">节点的副键。</param>
|
||||
/// <param name="key">节点的主键。</param>
|
||||
/// <param name="value">节点存储的值。</param>
|
||||
/// <param name="index">节点在跳跃表中的索引。</param>
|
||||
/// <param name="l">指向左侧节点的引用。</param>
|
||||
/// <param name="r">指向右侧节点的引用。</param>
|
||||
/// <param name="d">指向下一层节点的引用。</param>
|
||||
public SkipTableNode(long sortKey, long viceKey, long key, TValue value, int index,
|
||||
SkipTableNode<TValue> l,
|
||||
SkipTableNode<TValue> r,
|
||||
SkipTableNode<TValue> d)
|
||||
{
|
||||
Left = l;
|
||||
Right = r;
|
||||
Down = d;
|
||||
Value = value;
|
||||
Key = key;
|
||||
Index = index;
|
||||
SortKey = sortKey;
|
||||
ViceKey = viceKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9384bd7a2a46f401ea6de3461b5aa36b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Entitas.meta
vendored
Normal file
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Entitas.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 27462e8175ff84272b8b14c7b2ffb00c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Entitas/Component.meta
vendored
Normal file
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Entitas/Component.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e39207f0f9770415093af2f27fd7e988
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Entitas/Component/CoroutineLock.meta
vendored
Normal file
8
Assets/Scripts/ThirdParty/Fantasy.Unity/Runtime/Core/Entitas/Component/CoroutineLock.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db6a6392f9f6e4a2fbedb6ba3df377e8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user