This commit is contained in:
陈思海 2025-09-05 19:46:39 +08:00
parent 9fce987084
commit eb94e706df
854 changed files with 64984 additions and 0 deletions

8
Editor/CoreEditor.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 77313bd6dcc91f645a147a10abedadc3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e8ba5b17cffbb4ffea892f674fc8629f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,16 @@
using System;
using UnityEditor;
namespace Fantasy
{
internal static class CheckUnityVersion
{
[InitializeOnLoadMethod]
private static void OnInitializeOnLoad()
{
#if !UNITY_2021_3_OR_NEWER
Debug.LogError("Fantasy支持的最低版本为Unity2021.3.14f1c1");
#endif
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 455f338921e74471841971fd6b79db01
timeCreated: 1725943424

View File

@ -0,0 +1,49 @@
using System.IO;
using UnityEditor;
using UnityEngine;
namespace Fantasy
{
[InitializeOnLoad]
public static class FantasyStartup
{
private const string ScriptAssemblies = "Library/ScriptAssemblies/";
static FantasyStartup()
{
if (!FantasySettingsScriptableObject.Instance.autoCopyAssembly)
{
return;
}
var hotUpdatePath = FantasySettingsScriptableObject.Instance.hotUpdatePath;
if (string.IsNullOrEmpty(hotUpdatePath))
{
Debug.LogError("请先在菜单Fantasy-Fantasy Settings里设置HotUpdatePath目录位置");
return;
}
if (!Directory.Exists(hotUpdatePath))
{
Directory.CreateDirectory(hotUpdatePath);
}
// ReSharper disable once StringLastIndexOfIsCultureSpecific.1
if (hotUpdatePath.LastIndexOf("/") != hotUpdatePath.Length - 1)
{
FantasySettingsScriptableObject.Instance.hotUpdatePath += "/";
hotUpdatePath = FantasySettingsScriptableObject.Instance.hotUpdatePath;
}
foreach (var instanceHotUpdateAssemblyDefinition in FantasySettingsScriptableObject.Instance.hotUpdateAssemblyDefinitions)
{
var dll = instanceHotUpdateAssemblyDefinition.name;
File.Copy($"{ScriptAssemblies}{dll}.dll", $"{hotUpdatePath}/{dll}.dll.bytes", true);
File.Copy($"{ScriptAssemblies}{dll}.pdb", $"{hotUpdatePath}/{dll}.pdb.bytes", true);
}
AssetDatabase.Refresh();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 42156ba2865a4aa4a3e1e57b3ac9b984
timeCreated: 1688276977

View File

@ -0,0 +1,52 @@
using System.IO;
using UnityEditor;
using UnityEngine;
namespace Fantasy
{
public class LinkXmlGenerator
{
private const string LinkPath = "Assets/link.xml";
// 在Unity编辑器中运行该方法来生成link.xml文件
[UnityEditor.MenuItem("Fantasy/Generate link.xml")]
public static void GenerateLinkXml()
{
using (var writer = new StreamWriter("Assets/link.xml"))
{
writer.WriteLine("<linker>");
foreach (var assembly in FantasySettingsScriptableObject.Instance.includeAssembly)
{
GenerateLinkXml(writer, assembly, LinkPath);
Debug.Log($"{assembly} Link generation completed");
}
if (FantasySettingsScriptableObject.Instance?.linkAssemblyDefinitions != null)
{
foreach (var linkAssembly in FantasySettingsScriptableObject.Instance.linkAssemblyDefinitions)
{
GenerateLinkXml(writer, linkAssembly.name, LinkPath);
Debug.Log($"{linkAssembly.name} Link generation completed");
}
}
writer.WriteLine("</linker>");
}
AssetDatabase.Refresh();
Debug.Log("link.xml generated successfully!");
}
private static void GenerateLinkXml(StreamWriter writer, string assemblyName, string outputPath)
{
var assembly = System.Reflection.Assembly.Load(assemblyName);
var types = assembly.GetTypes();
writer.WriteLine($" <assembly fullname=\"{assembly.GetName().Name}\">");
foreach (var type in types)
{
var typeName = type.FullName.Replace('<', '+').Replace('>', '+');
writer.WriteLine($" <type fullname=\"{typeName}\" preserve=\"all\"/>");
}
writer.WriteLine(" </assembly>");
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cda4c9403de946df9c31654416193a21
timeCreated: 1722743236

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3a6997d946f3400e8c423fe1b9245f65
timeCreated: 1688277110

View File

@ -0,0 +1,13 @@
using UnityEditor;
namespace Fantasy
{
public class FantasySettings
{
[MenuItem("Fantasy/Fantasy Settings")]
public static void OpenFantasySettings()
{
SettingsService.OpenProjectSettings("Project/Fantasy Settings");
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 977a7c172c30403da60286ba39b7bc72
timeCreated: 1686913667

View File

@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace Fantasy
{
public class FantasySettingsProvider : SettingsProvider
{
private SerializedObject _serializedObject;
private SerializedProperty _autoCopyAssembly;
private SerializedProperty _hotUpdatePath;
private SerializedProperty _hotUpdateAssemblyDefinitions;
private SerializedProperty _linkAssemblyDefinitions;
private SerializedProperty _includeAssembly;
public FantasySettingsProvider() : base("Project/Fantasy Settings", SettingsScope.Project) { }
public override void OnActivate(string searchContext, VisualElement rootElement)
{
Init();
base.OnActivate(searchContext, rootElement);
}
public override void OnDeactivate()
{
base.OnDeactivate();
FantasySettingsScriptableObject.Save();
}
private void Init()
{
_serializedObject?.Dispose();
_serializedObject = new SerializedObject(FantasySettingsScriptableObject.Instance);
_autoCopyAssembly = _serializedObject.FindProperty("autoCopyAssembly");
_hotUpdatePath = _serializedObject.FindProperty("hotUpdatePath");
_hotUpdateAssemblyDefinitions = _serializedObject.FindProperty("hotUpdateAssemblyDefinitions");
_linkAssemblyDefinitions = _serializedObject.FindProperty("linkAssemblyDefinitions");
_includeAssembly = _serializedObject.FindProperty("includeAssembly");
}
public override void OnGUI(string searchContext)
{
if (_serializedObject == null || !_serializedObject.targetObject)
{
Init();
}
using (CreateSettingsWindowGUIScope())
{
_serializedObject!.Update();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(_autoCopyAssembly);
EditorGUILayout.PropertyField(_hotUpdatePath);
EditorGUILayout.PropertyField(_hotUpdateAssemblyDefinitions);
EditorGUILayout.PropertyField(_includeAssembly);
EditorGUILayout.PropertyField(_linkAssemblyDefinitions);
// EditorGUILayout.HelpBox("默认包括Fantasy.Unity所以不需要再次指定。", MessageType.Info);
if (GUILayout.Button("GenerateLinkXml"))
{
LinkXmlGenerator.GenerateLinkXml();
}
if (EditorGUI.EndChangeCheck())
{
_serializedObject.ApplyModifiedProperties();
FantasySettingsScriptableObject.Save();
EditorApplication.RepaintHierarchyWindow();
}
base.OnGUI(searchContext);
}
}
private IDisposable CreateSettingsWindowGUIScope()
{
var unityEditorAssembly = System.Reflection.Assembly.GetAssembly(typeof(EditorWindow));
var type = unityEditorAssembly.GetType("UnityEditor.SettingsWindow+GUIScope");
return Activator.CreateInstance(type) as IDisposable;
}
static FantasySettingsProvider _provider;
[SettingsProvider]
public static SettingsProvider CreateMyCustomSettingsProvider()
{
if (FantasySettingsScriptableObject.Instance && _provider == null)
{
_provider = new FantasySettingsProvider();
using (var so = new SerializedObject(FantasySettingsScriptableObject.Instance))
{
_provider.keywords = GetSearchKeywordsFromSerializedObject(so);
}
}
return _provider;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 047b2f13e73f413fa000bf7be979fb4a
timeCreated: 1688380387

View File

@ -0,0 +1,25 @@
using UnityEditor;
using UnityEditor.Compilation;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Serialization;
namespace Fantasy
{
[ScriptableObjectPath("ProjectSettings/FantasySettings.asset")]
public class FantasySettingsScriptableObject : ScriptableObjectSingleton<FantasySettingsScriptableObject>, ISerializationCallbackReceiver
{
[FormerlySerializedAs("AutoCopyAssembly")] [Header("自动拷贝程序集到HotUpdatePath目录中")]
public bool autoCopyAssembly = false;
[FormerlySerializedAs("HotUpdatePath")] [Header("HotUpdate目录(Unity编译后会把所有HotUpdate程序集Copy一份到这个目录下)")]
public string hotUpdatePath;
[FormerlySerializedAs("HotUpdateAssemblyDefinitions")] [Header("HotUpdate程序集")]
public AssemblyDefinitionAsset[] hotUpdateAssemblyDefinitions;
[FormerlySerializedAs("LinkAssemblyDefinitions")] [Header("指定要生成Link.xml的程序集")]
public AssemblyDefinitionAsset[] linkAssemblyDefinitions;
[FormerlySerializedAs("IncludeAssembly")] [Header("生成Link.xml时候默认包含的程序集")]
public string[] includeAssembly = new[] { "Assembly-CSharp", "Fantasy.Unity" };
public void OnBeforeSerialize() { }
public void OnAfterDeserialize() { }
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 27a37e930ca3454fb57bc895f50d2106
timeCreated: 1688277120

View File

@ -0,0 +1,100 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEditorInternal;
using UnityEngine;
// ReSharper disable AssignNullToNotNullAttribute
namespace Fantasy
{
public class ScriptableObjectSingleton<T> : ScriptableObject where T : ScriptableObject
{
private static T _instance;
public static T Instance
{
get
{
if (_instance == null)
{
_instance = Load();
}
return _instance;
}
}
private static T Load()
{
var scriptableObjectPath = GetScriptableObjectPath();
if (string.IsNullOrEmpty(scriptableObjectPath))
{
return null;
}
var loadSerializedFileAndForget = InternalEditorUtility.LoadSerializedFileAndForget(scriptableObjectPath);
if (loadSerializedFileAndForget.Length <= 0)
{
return CreateInstance<T>();
}
return loadSerializedFileAndForget[0] as T;
}
public static void Save(bool saveAsText = true)
{
if (_instance == null)
{
Debug.LogError("Cannot save ScriptableObjectSingleton: no instance!");
return;
}
var scriptableObjectPath = GetScriptableObjectPath();
if (string.IsNullOrEmpty(scriptableObjectPath))
{
return;
}
var directoryName = Path.GetDirectoryName(scriptableObjectPath);
if (!Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
}
UnityEngine.Object[] obj = { _instance };
InternalEditorUtility.SaveToSerializedFileAndForget(obj, scriptableObjectPath, saveAsText);
}
private static string GetScriptableObjectPath()
{
var scriptableObjectPathAttribute = typeof(T).GetCustomAttribute(typeof(ScriptableObjectPathAttribute)) as ScriptableObjectPathAttribute;
return scriptableObjectPathAttribute?.ScriptableObjectPath;
}
}
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class ScriptableObjectPathAttribute : Attribute
{
internal readonly string ScriptableObjectPath;
public ScriptableObjectPathAttribute(string scriptableObjectPath)
{
if (string.IsNullOrEmpty(scriptableObjectPath))
{
throw new ArgumentException("Invalid relative path (it is empty)");
}
if (scriptableObjectPath[0] == '/')
{
scriptableObjectPath = scriptableObjectPath.Substring(1);
}
ScriptableObjectPath = scriptableObjectPath;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3c77f5208dc14542ae7497d59321ef76
timeCreated: 1688278016

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b9e5c7d1436ec414fa3f69a23aaafc3b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,229 @@
using UnityEngine;
using UnityEditor;
using UnityEngine.Networking;
using System.IO;
using System;
namespace UnityWebSocket.Editor
{
internal class SettingsWindow : EditorWindow
{
static SettingsWindow window = null;
[MenuItem("Tools/UnityWebSocket", priority = 100)]
internal static void Open()
{
if (window != null)
{
window.Close();
}
window = GetWindow<SettingsWindow>(true, "UnityWebSocket");
window.minSize = window.maxSize = new Vector2(600, 310);
window.Show();
window.BeginCheck();
}
private void OnGUI()
{
DrawLogo();
DrawVersion();
DrawSeparator(80);
DrawSeparator(186);
DrawHelper();
DrawFooter();
}
Texture2D logoTex = null;
private void DrawLogo()
{
if (logoTex == null)
{
logoTex = new Texture2D(66, 66);
logoTex.LoadImage(Convert.FromBase64String(LOGO_BASE64.VALUE));
for (int i = 0; i < 66; i++) for (int j = 0; j < 15; j++) logoTex.SetPixel(i, j, Color.clear);
logoTex.Apply();
}
var logoPos = new Rect(10, 10, 66, 66);
GUI.DrawTexture(logoPos, logoTex);
var title = "<color=#3A9AD8><b>UnityWebSocket</b></color>";
var titlePos = new Rect(80, 20, 500, 50);
GUI.Label(titlePos, title, TextStyle(24));
}
private void DrawSeparator(int y)
{
EditorGUI.DrawRect(new Rect(10, y, 580, 1), Color.white * 0.5f);
}
private GUIStyle TextStyle(int fontSize = 10, TextAnchor alignment = TextAnchor.UpperLeft, float alpha = 0.85f)
{
var style = new GUIStyle();
style.fontSize = fontSize;
style.normal.textColor = (EditorGUIUtility.isProSkin ? Color.white : Color.black) * alpha;
style.alignment = alignment;
style.richText = true;
return style;
}
private void DrawVersion()
{
GUI.Label(new Rect(440, 10, 150, 10), "Current Version: " + Settings.VERSION, TextStyle(alignment: TextAnchor.MiddleLeft));
if (string.IsNullOrEmpty(latestVersion))
{
GUI.Label(new Rect(440, 30, 150, 10), "Checking for Updates...", TextStyle(alignment: TextAnchor.MiddleLeft));
}
else if (latestVersion == "unknown")
{
}
else
{
GUI.Label(new Rect(440, 30, 150, 10), "Latest Version: " + latestVersion, TextStyle(alignment: TextAnchor.MiddleLeft));
if (Settings.VERSION == latestVersion)
{
if (GUI.Button(new Rect(440, 50, 150, 18), "Check Update"))
{
latestVersion = "";
changeLog = "";
BeginCheck();
}
}
else
{
if (GUI.Button(new Rect(440, 50, 150, 18), "Update to | " + latestVersion))
{
ShowUpdateDialog();
}
}
}
}
private void ShowUpdateDialog()
{
var isOK = EditorUtility.DisplayDialog("UnityWebSocket",
"Update UnityWebSocket now?\n" + changeLog,
"Update Now", "Cancel");
if (isOK)
{
UpdateVersion();
}
}
private void UpdateVersion()
{
Application.OpenURL(Settings.GITHUB + "/releases");
}
private void DrawHelper()
{
GUI.Label(new Rect(330, 200, 100, 18), "GitHub:", TextStyle(10, TextAnchor.MiddleRight));
if (GUI.Button(new Rect(440, 200, 150, 18), "UnityWebSocket"))
{
Application.OpenURL(Settings.GITHUB);
}
GUI.Label(new Rect(330, 225, 100, 18), "Report:", TextStyle(10, TextAnchor.MiddleRight));
if (GUI.Button(new Rect(440, 225, 150, 18), "Report an Issue"))
{
Application.OpenURL(Settings.GITHUB + "/issues/new");
}
GUI.Label(new Rect(330, 250, 100, 18), "Email:", TextStyle(10, TextAnchor.MiddleRight));
if (GUI.Button(new Rect(440, 250, 150, 18), Settings.EMAIL))
{
var uri = new Uri(string.Format("mailto:{0}?subject={1}", Settings.EMAIL, "UnityWebSocket Feedback"));
Application.OpenURL(uri.AbsoluteUri);
}
GUI.Label(new Rect(330, 275, 100, 18), "QQ群:", TextStyle(10, TextAnchor.MiddleRight));
if (GUI.Button(new Rect(440, 275, 150, 18), Settings.QQ_GROUP))
{
Application.OpenURL(Settings.QQ_GROUP_LINK);
}
}
private void DrawFooter()
{
EditorGUI.DropShadowLabel(new Rect(10, 230, 400, 20), "Developed by " + Settings.AUHTOR);
EditorGUI.DropShadowLabel(new Rect(10, 250, 400, 20), "All rights reserved");
}
UnityWebRequest req;
string changeLog = "";
string latestVersion = "";
void BeginCheck()
{
EditorApplication.update -= VersionCheckUpdate;
EditorApplication.update += VersionCheckUpdate;
req = UnityWebRequest.Get(Settings.GITHUB + "/releases/latest");
req.SendWebRequest();
}
private void VersionCheckUpdate()
{
#if UNITY_2020_3_OR_NEWER
if (req == null
|| req.result == UnityWebRequest.Result.ConnectionError
|| req.result == UnityWebRequest.Result.DataProcessingError
|| req.result == UnityWebRequest.Result.ProtocolError)
#elif UNITY_2018_1_OR_NEWER
if (req == null || req.isNetworkError || req.isHttpError)
#else
if (req == null || req.isError)
#endif
{
EditorApplication.update -= VersionCheckUpdate;
latestVersion = "unknown";
return;
}
if (req.isDone)
{
EditorApplication.update -= VersionCheckUpdate;
latestVersion = req.url.Substring(req.url.LastIndexOf("/") + 1).TrimStart('v');
if (Settings.VERSION != latestVersion)
{
var text = req.downloadHandler.text;
var st = text.IndexOf("content=\"" + latestVersion);
st = st > 0 ? text.IndexOf("\n", st) : -1;
var end = st > 0 ? text.IndexOf("\" />", st) : -1;
if (st > 0 && end > st)
{
changeLog = text.Substring(st + 1, end - st - 1).Trim();
changeLog = changeLog.Replace("\r", "");
changeLog = changeLog.Replace("\n", "\n- ");
changeLog = "\nCHANGE LOG: \n- " + changeLog + "\n";
}
}
Repaint();
}
}
}
internal static class LOGO_BASE64
{
internal const string VALUE = "iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAMAAADUivDaAAAAq1BMVEUAAABKmtcvjtYzl" +
"9szmNszl9syl9k0mNs0mNwzmNs0mNszl9szl9s0mNs0mNwzmNw0mNwyltk0mNw0mNwzl9s0mNsymNs0mNszmNwzmNwzm" +
"NszmNs0mNwzl9w0mNwzmNw0mNs0mNs0mNwzl9wzmNs0mNwzmNs0mNwzl90zmNszmNszl9szmNsxmNszmNszmNw0mNwzm" +
"Nw0mNs2neM4pe41mt43ouo2oOY5qfM+UHlaAAAAMnRSTlMAAwXN3sgI+/069MSCK6M/MA74h9qfFHB8STWMJ9OSdmNcI" +
"8qya1IeF+/U0EIa57mqmFTYJe4AAAN3SURBVFjD7ZbpkppAFEa/bgVBREF2kEVGFNeZsM77P1kadURnJkr8k1Qlx1Khu" +
"/pw7+2lwH/+YcgfMBBLG7VocwDamzH+wJBB8Qhjve2f0TdrGwjei6o4Ub/nM/APw5Z7vvSB/qrCrqbD6fBEVtigeMxks" +
"fX9zWbj+z1jhqgSBplQ50eGo4614WXlRAzgrRhmtSfvxAn7pB0N5ObaKKZZuU5/d37IBcBgUQwqDuf7Z2gUmVAl4NGNr" +
"/UeHxV5n39ulbaKLI86h6HilmM5M1aN126lpNhtl59yeTsp8nUMvpNC1J3bh5FtfVRk+bJrJunn5d4U4piJ/Vw9eXgsj" +
"4ZpZaCjg9waZkIpnBWLJ44OwoNu60F2UnSaEkKv4XnAlCpm6B4F/aKMDiyGi2L8SEEAVdxNLuzmgV7nFwObEe2xQVuX+" +
"RV1lWetga3w+cN1sXgvm4cJH8OEgZC1DPKhfF/BIymmQrMjq/x65FUeEkDup8GxoexZmznHCvANtXU/CAq13yimhQGtm" +
"H4VCPnBBL1fTKo3CqEcvq7Lb/OwHxWTYlyw+JmjKoVvDLVOQB4pVsM8K8smgvLCxZDlIijwyOEc+nr/msMwK0+GQWGBd" +
"tmhjv8icTds1s2ammaFh04QLLe69NK7guP6mTDMaw3o6nAX/Z7EXUskPSvWEWg4srVlp5NTDXv9Lce9HGN5eeG4nj5Yz" +
"ACteU2wQLo4MBtJfd1nw5nG1/s9zwUQ6pykL1TQjqdeuvQW0naz2XKLYL4Cwzr4vj+OQdD96CSp7Lrynp4aeFF0xdm5q" +
"6OFtFfPv7URxpWJNjd/N+3+I9+1klMav12Qtgbt9R2JaIopjkzaPtOFq4KxUpqfUMSFnQrySWjLoQzRZS4HMH84ME1ej" +
"S1YJpQZ3B+sR1uCQJSBdGdCk1eAEgORR88KK05W8dh2MA+A/SKCYu3mCJ0Ek7HBx4HHeuwYy5G3x8hSMTJcOMFbinCsn" +
"hO1V1aszGULvA0g4UFsb4VA0hAFcyo6cgLsAoT7uUtGAH5wQKQle0wuLyxLTaNyJEYwxw4wSljLK1TP8CAaOyhBMMEsj" +
"OBoXgo7VGElFkSWL+vef1RF2YNXeRWYzQBTpkhC8KaZHhuIogArkQLKClBZjU26B2IZgGz+cpZkHl8g3fYUaW/YP2kb2" +
"M/V97JY/vZN859n+QmO7XtC9Bf2jAAAAABJRU5ErkJggg==";
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 902614e06186a482f9e816e1d1984547
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
Runtime/CoreRuntime.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dcbd0e41955f82b45bab54ebc1183deb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4e4623e6f2b7ffd41b068edd3c9568d9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5006194ae42edaa4bb33cb6172c123ab
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Fantasy.DataStructure.Collection;
// ReSharper disable CollectionNeverQueried.Global
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
namespace Fantasy.Assembly
{
/// <summary>
/// AssemblyInfo提供有关程序集和类型的信息
/// </summary>
public sealed class AssemblyInfo
{
/// <summary>
/// 唯一标识
/// </summary>
public readonly long AssemblyIdentity;
/// <summary>
/// 获取或设置与此程序集相关联的 <see cref="Assembly"/> 实例。
/// </summary>
public System.Reflection.Assembly Assembly { get; private set; }
/// <summary>
/// 程序集类型集合,获取一个列表,包含从程序集加载的所有类型。
/// </summary>
public readonly List<Type> AssemblyTypeList = new List<Type>();
/// <summary>
/// 程序集类型分组集合,获取一个分组列表,将接口类型映射到实现这些接口的类型。
/// </summary>
public readonly OneToManyList<Type, Type> AssemblyTypeGroupList = new OneToManyList<Type, Type>();
/// <summary>
/// 初始化 <see cref="AssemblyInfo"/> 类的新实例。
/// </summary>
/// <param name="assemblyIdentity"></param>
public AssemblyInfo(long assemblyIdentity)
{
AssemblyIdentity = assemblyIdentity;
}
/// <summary>
/// 从指定的程序集加载类型信息并进行分类。
/// </summary>
/// <param name="assembly">要加载信息的程序集。</param>
public void Load(System.Reflection.Assembly assembly)
{
Assembly = assembly;
var assemblyTypes = assembly.GetTypes().ToList();
foreach (var type in assemblyTypes)
{
if (type.IsAbstract || type.IsInterface)
{
continue;
}
var interfaces = type.GetInterfaces();
foreach (var interfaceType in interfaces)
{
AssemblyTypeGroupList.Add(interfaceType, type);
}
}
AssemblyTypeList.AddRange(assemblyTypes);
}
/// <summary>
/// 重新加载程序集的类型信息。
/// </summary>
/// <param name="assembly"></param>
public void ReLoad(System.Reflection.Assembly assembly)
{
Unload();
Load(assembly);
}
/// <summary>
/// 卸载程序集的类型信息。
/// </summary>
public void Unload()
{
AssemblyTypeList.Clear();
AssemblyTypeGroupList.Clear();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6c962fd73de88d64fa9d1ab97ed270b6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,285 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using Fantasy.Async;
using Fantasy.Helper;
#pragma warning disable CS8604 // Possible null reference argument.
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8603
#pragma warning disable CS8618
namespace Fantasy.Assembly
{
/// <summary>
/// 管理程序集加载和卸载的帮助类。
/// </summary>
public static class AssemblySystem
{
#if FANTASY_WEBGL
private static readonly List<IAssembly> AssemblySystems = new List<IAssembly>();
private static readonly Dictionary<long, AssemblyInfo> AssemblyList = new Dictionary<long, AssemblyInfo>();
#else
private static readonly ConcurrentQueue<IAssembly> AssemblySystems = new ConcurrentQueue<IAssembly>();
private static readonly ConcurrentDictionary<long, AssemblyInfo> AssemblyList = new ConcurrentDictionary<long, AssemblyInfo>();
#endif
/// <summary>
/// 初始化 AssemblySystem。仅限内部
/// </summary>
/// <param name="assemblies"></param>
internal static async FTask InnerInitialize(params System.Reflection.Assembly[] assemblies)
{
await LoadAssembly(typeof(AssemblySystem).Assembly);
foreach (var assembly in assemblies)
{
await LoadAssembly(assembly);
}
}
/// <summary>
/// 加载指定的程序集,并触发相应的事件。
/// </summary>
/// <param name="assembly">要加载的程序集。</param>
/// <param name="isCurrentDomain">如果当前Domain中已经存在同名的Assembly,使用Domain中的程序集。</param>
public static async FTask LoadAssembly(System.Reflection.Assembly assembly, bool isCurrentDomain = true)
{
if (isCurrentDomain)
{
var currentDomainAssemblies = System.AppDomain.CurrentDomain.GetAssemblies();
var currentAssembly = currentDomainAssemblies.FirstOrDefault(d => d.GetName().Name == assembly.GetName().Name);
if (currentAssembly != null)
{
assembly = currentAssembly;
}
}
var assemblyIdentity = AssemblyIdentity(assembly);
if (AssemblyList.TryGetValue(assemblyIdentity, out var assemblyInfo))
{
assemblyInfo.ReLoad(assembly);
foreach (var assemblySystem in AssemblySystems)
{
await assemblySystem.ReLoad(assemblyIdentity);
}
}
else
{
assemblyInfo = new AssemblyInfo(assemblyIdentity);
assemblyInfo.Load(assembly);
AssemblyList.TryAdd(assemblyIdentity, assemblyInfo);
foreach (var assemblySystem in AssemblySystems)
{
await assemblySystem.Load(assemblyIdentity);
}
}
}
/// <summary>
/// 卸载程序集
/// </summary>
/// <param name="assembly"></param>
public static async FTask UnLoadAssembly(System.Reflection.Assembly assembly)
{
var assemblyIdentity = AssemblyIdentity(assembly);
if (!AssemblyList.Remove(assemblyIdentity, out var assemblyInfo))
{
return;
}
assemblyInfo.Unload();
foreach (var assemblySystem in AssemblySystems)
{
await assemblySystem.OnUnLoad(assemblyIdentity);
}
}
/// <summary>
/// 将AssemblySystem接口的object注册到程序集管理中心
/// </summary>
/// <param name="obj"></param>
public static async FTask Register(object obj)
{
if (obj is not IAssembly assemblySystem)
{
return;
}
#if FANTASY_WEBGL
AssemblySystems.Add(assemblySystem);
#else
AssemblySystems.Enqueue(assemblySystem);
#endif
foreach (var (assemblyIdentity, _) in AssemblyList)
{
await assemblySystem.Load(assemblyIdentity);
}
}
/// <summary>
/// 程序集管理中心卸载注册的Load、ReLoad、UnLoad的接口
/// </summary>
/// <param name="obj"></param>
public static void UnRegister(object obj)
{
if (obj is not IAssembly assemblySystem)
{
return;
}
#if FANTASY_WEBGL
AssemblySystems.Remove(assemblySystem);
#else
var count = AssemblySystems.Count;
for (var i = 0; i < count; i++)
{
if (!AssemblySystems.TryDequeue(out var removeAssemblySystem))
{
continue;
}
if (removeAssemblySystem == assemblySystem)
{
break;
}
AssemblySystems.Enqueue(removeAssemblySystem);
}
#endif
}
/// <summary>
/// 获取所有已加载程序集中的所有类型。
/// </summary>
/// <returns>所有已加载程序集中的类型。</returns>
public static IEnumerable<Type> ForEach()
{
foreach (var (_, assemblyInfo) in AssemblyList)
{
foreach (var type in assemblyInfo.AssemblyTypeList)
{
yield return type;
}
}
}
/// <summary>
/// 获取指定程序集中的所有类型。
/// </summary>
/// <param name="assemblyIdentity">程序集唯一标识。</param>
/// <returns>指定程序集中的类型。</returns>
public static IEnumerable<Type> ForEach(long assemblyIdentity)
{
if (!AssemblyList.TryGetValue(assemblyIdentity, out var assemblyInfo))
{
yield break;
}
foreach (var type in assemblyInfo.AssemblyTypeList)
{
yield return type;
}
}
/// <summary>
/// 获取所有已加载程序集中实现指定类型的所有类型。
/// </summary>
/// <param name="findType">要查找的基类或接口类型。</param>
/// <returns>所有已加载程序集中实现指定类型的类型。</returns>
public static IEnumerable<Type> ForEach(Type findType)
{
foreach (var (_, assemblyInfo) in AssemblyList)
{
if (!assemblyInfo.AssemblyTypeGroupList.TryGetValue(findType, out var assemblyLoad))
{
continue;
}
foreach (var type in assemblyLoad)
{
yield return type;
}
}
}
/// <summary>
/// 获取指定程序集中实现指定类型的所有类型。
/// </summary>
/// <param name="assemblyIdentity">程序集唯一标识。</param>
/// <param name="findType">要查找的基类或接口类型。</param>
/// <returns>指定程序集中实现指定类型的类型。</returns>
public static IEnumerable<Type> ForEach(long assemblyIdentity, Type findType)
{
if (!AssemblyList.TryGetValue(assemblyIdentity, out var assemblyInfo))
{
yield break;
}
if (!assemblyInfo.AssemblyTypeGroupList.TryGetValue(findType, out var assemblyLoad))
{
yield break;
}
foreach (var type in assemblyLoad)
{
yield return type;
}
}
/// <summary>
/// 获取指定程序集的实例。
/// </summary>
/// <param name="assemblyIdentity">程序集名称。</param>
/// <returns>指定程序集的实例,如果未加载则返回 null。</returns>
public static System.Reflection.Assembly GetAssembly(long assemblyIdentity)
{
return !AssemblyList.TryGetValue(assemblyIdentity, out var assemblyInfo) ? null : assemblyInfo.Assembly;
}
/// <summary>
/// 获取当前框架注册的Assembly
/// </summary>
/// <returns></returns>
public static IEnumerable<System.Reflection.Assembly> ForEachAssembly
{
get
{
foreach (var (_, assemblyInfo) in AssemblyList)
{
yield return assemblyInfo.Assembly;
}
}
}
/// <summary>
/// 根据Assembly的强命名计算唯一标识。
/// </summary>
/// <param name="assembly"></param>
/// <returns></returns>
private static long AssemblyIdentity(System.Reflection.Assembly assembly)
{
return HashCodeHelper.ComputeHash64(assembly.GetName().Name);
}
/// <summary>
/// 释放资源,卸载所有加载的程序集。
/// </summary>
public static void Dispose()
{
DisposeAsync().Coroutine();
}
private static async FTask DisposeAsync()
{
foreach (var (_, assemblyInfo) in AssemblyList.ToArray())
{
await UnLoadAssembly(assemblyInfo.Assembly);
}
AssemblyList.Clear();
AssemblySystems.Clear();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 533740363a43b98499d20ddab704ae7b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,27 @@
using System;
using Fantasy.Async;
namespace Fantasy.Assembly
{
/// <summary>
/// 实现这个接口、会再程序集首次加载、卸载、重载的时候调用
/// </summary>
public interface IAssembly : IDisposable
{
/// <summary>
/// 程序集加载时调用
/// </summary>
/// <param name="assemblyIdentity">程序集标识</param>
public FTask Load(long assemblyIdentity);
/// <summary>
/// 程序集重新加载的时候调用
/// </summary>
/// <param name="assemblyIdentity">程序集标识</param>
public FTask ReLoad(long assemblyIdentity);
/// <summary>
/// 卸载的时候调用
/// </summary>
/// <param name="assemblyIdentity">程序集标识</param>
public FTask OnUnLoad(long assemblyIdentity);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2265dda8f756ced42b346d101b5c431c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b9672ac65f515574a8e888c0baa4f5a2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dcf9f758978488e4d94d0aec6c151e3f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0584f8f7232c13e4a9043b14fcb00ba6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 158dbd1e6090fc6408b93c074e56a894
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a76811d05f24f5449bdc0fe27e6f7b8e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1630d7e4e5e733d439ab42b7e57fee87
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 396ddd0da4230db44a504522fb7a19f1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bd87cec78544de449a1d55d76b07526f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ee2297376a867104f9df48c053f5324c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9dfe758585a734d45a3b35ea954527e8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: edf0f604fb0ac08419b517354597a3a3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d54ccbb980880d4491058a858d80bb7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 495d546594e267d478017eb02d4bc0c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5838abe5331703b418048351793b9f06
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 712cb2a78894b5640a8349e843c5e60a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a56b47840004ebf449a2119174097b43
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 93ee84e42c94b1b4cb02e01fe4e71e4e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d66f816fefb57054ebc073ab58b54d1f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: afa0d451378b33b45a4371cf99ade9a5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a674c814e81ddab4ba537637cfa43782
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b6ce6cc73a185874caf2b74bd7d21e37
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 036cd28ac2417c14682d7d23cfb70e1c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 03783538a8b735243a1716efa6f8878a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 518d4d5f27703994f92049cd720c7215
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 68c7a566ce8a92e4193abe208024aad4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 89e054e94e110024c8a3a11f3d9a2f07
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b52b6117133f5ea4b8cf9dbbea4f014b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 86f203177e838a04a9a89fda04b71145
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a60895bae61946545ab19518a97ea7b7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cf712318b312bb946a4d43746f3c11f4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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 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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b87d760d8e818a848b9da0812a318d6a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c6c026f7470b12d4a9bddbbce6dd662c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a2be747db231b534b91e0c7fd92c6999
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5d9ea79288d88e040bef0c36410e8617
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 16e00a9de7378c0428b1dbc258459183
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8271cef0217701d4d8a019bb9977d29c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 40753c1a6c88fcf4e82db32f1f4ba4a5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dd88ad42b48be2a498a7563a1a5cda20
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 83a2a26ccc53b2944b584c0f00010a77
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 09af986a342b3e849baefbf4ac89d3a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More