Compare commits
13 Commits
b6439492df
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| fd68071416 | |||
| a530eba460 | |||
| 32f0038e34 | |||
| c12d4a3754 | |||
| a3483f23d8 | |||
| 3db8bde1c2 | |||
| 223df3fb91 | |||
| 8e419a3257 | |||
| 48965decbb | |||
| 172b2af3ea | |||
| 5cc1d2aabc | |||
| 0b562b7f65 | |||
| ccb51f26c3 |
@@ -109,6 +109,7 @@ namespace Convention.Experimental.Modules
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("This object does not belong to any pool.", instance);
|
||||
throw new PublicType.GameException("This object does not belong to any pool.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ MonoBehaviour:
|
||||
assemblyNames:
|
||||
- Assembly-CSharp
|
||||
- Assembly-CSharp-firstpass
|
||||
- Cinemachine
|
||||
- Dreamteck.Splines
|
||||
- Dreamteck.Utilities
|
||||
- EasySave3
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Convention.WindowsUI;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -8,9 +7,16 @@ using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Convention.WindowsUI;
|
||||
using Demo.Game;
|
||||
using Unity.Collections;
|
||||
using UnityEditor;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor.Build;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
|
||||
@@ -1268,7 +1274,11 @@ namespace Convention
|
||||
#endif
|
||||
public static void InitExtensionEnv()
|
||||
{
|
||||
UnityEngine.Application.quitting += () => CoroutineStarter = null;
|
||||
UnityEngine.Application.quitting += () =>
|
||||
{
|
||||
GameObject.Destroy(s_CoroutineStarter.gameObject);
|
||||
s_CoroutineStarter = null;
|
||||
};
|
||||
|
||||
InitExtensionEnvCalls();
|
||||
GlobalConfig.InitExtensionEnv();
|
||||
@@ -1282,27 +1292,82 @@ namespace Convention
|
||||
return MainThreadID == Thread.CurrentThread.ManagedThreadId;
|
||||
}
|
||||
|
||||
private static CoroutineMonoStarterUtil CoroutineStarter;
|
||||
private static CoroutineMonoStarterUtil s_CoroutineStarter;
|
||||
private static CoroutineMonoStarterUtil CoroutineStarter
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_CoroutineStarter == null)
|
||||
{
|
||||
s_CoroutineStarter = new GameObject($"{nameof(ConventionUtility)}-{nameof(CoroutineStarter)}").AddComponent<CoroutineMonoStarterUtil>();
|
||||
}
|
||||
return s_CoroutineStarter;
|
||||
}
|
||||
}
|
||||
|
||||
public static GameObject Singleton => CoroutineStarter.gameObject;
|
||||
|
||||
private class CoroutineMonoStarterUtil : MonoBehaviour
|
||||
{
|
||||
internal float waitClock;
|
||||
private void Update()
|
||||
{
|
||||
waitClock = Time.realtimeSinceStartup;
|
||||
MainThreadID = Thread.CurrentThread.ManagedThreadId;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
CoroutineStarter = null;
|
||||
s_CoroutineStarter = null;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 包装即将用于协程的迭代器成为一个防止假死机的迭代器
|
||||
/// </summary>
|
||||
/// <param name="ir"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerator AvoidFakeStop(IEnumerator ir, float fps = 5)
|
||||
{
|
||||
Stack<IEnumerator> loadingTask = new();
|
||||
loadingTask.Push(ir);
|
||||
float maxWaitClock = 1 / fps;
|
||||
while (loadingTask.Count > 0)
|
||||
{
|
||||
// 防止大量无延迟函数的使用导致假死机
|
||||
//if (Time.realtimeSinceStartup - CoroutineStarter.waitClock > maxWaitClock)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
if (loadingTask.Peek().MoveNext())
|
||||
{
|
||||
if (loadingTask.Peek().Current is IEnumerator next)
|
||||
loadingTask.Push(next);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadingTask.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 包装即将用于协程的迭代器成为一个防止假死机的迭代器
|
||||
/// </summary>
|
||||
/// <param name="ir"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerator AvoidFakeStopWithoutDeepStack(IEnumerator ir, float fps = 5)
|
||||
{
|
||||
float maxWaitClock = 1 / fps;
|
||||
while (ir.MoveNext())
|
||||
{
|
||||
// 防止大量无延迟函数的使用导致假死机
|
||||
if (Time.realtimeSinceStartup - CoroutineStarter.waitClock > maxWaitClock)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
public static Coroutine StartCoroutine(IEnumerator coroutine)
|
||||
{
|
||||
if (CoroutineStarter == null)
|
||||
{
|
||||
CoroutineStarter = new GameObject($"{nameof(ConventionUtility)}-{nameof(CoroutineStarter)}").AddComponent<CoroutineMonoStarterUtil>();
|
||||
}
|
||||
return CoroutineStarter.StartCoroutine(coroutine);
|
||||
}
|
||||
public static void CloseCoroutine(Coroutine coroutine)
|
||||
@@ -1316,28 +1381,30 @@ namespace Convention
|
||||
|
||||
public class ActionStepCoroutineWrapper
|
||||
{
|
||||
private class YieldInstructionWrapper
|
||||
private struct YieldInstructionWrapper
|
||||
{
|
||||
public YieldInstruction UnityYieldInstruction;
|
||||
public CustomYieldInstruction CustomYieldInstruction;
|
||||
|
||||
public YieldInstructionWrapper()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public YieldInstructionWrapper(YieldInstruction unityYieldInstruction)
|
||||
public YieldInstructionWrapper(YieldInstruction unityYieldInstruction, CustomYieldInstruction customYieldInstruction)
|
||||
{
|
||||
this.UnityYieldInstruction = unityYieldInstruction;
|
||||
this.CustomYieldInstruction = customYieldInstruction;
|
||||
}
|
||||
|
||||
public YieldInstructionWrapper(CustomYieldInstruction customYieldInstruction)
|
||||
public static YieldInstructionWrapper Make(YieldInstruction unityYieldInstruction)
|
||||
{
|
||||
this.CustomYieldInstruction = customYieldInstruction;
|
||||
return new(unityYieldInstruction, null);
|
||||
}
|
||||
|
||||
public static YieldInstructionWrapper Make(CustomYieldInstruction customYieldInstruction)
|
||||
{
|
||||
return new(null, customYieldInstruction);
|
||||
}
|
||||
}
|
||||
|
||||
private List<KeyValuePair<YieldInstructionWrapper, Action>> steps = new();
|
||||
|
||||
public ActionStepCoroutineWrapper Update(Action action)
|
||||
{
|
||||
steps.Add(new(new(), action));
|
||||
@@ -1345,33 +1412,33 @@ namespace Convention
|
||||
}
|
||||
public ActionStepCoroutineWrapper Wait(float time, Action action)
|
||||
{
|
||||
steps.Add(new(new(new WaitForSeconds(time)), action));
|
||||
steps.Add(new(YieldInstructionWrapper.Make(new WaitForSeconds(time)), action));
|
||||
return this;
|
||||
}
|
||||
public ActionStepCoroutineWrapper FixedUpdate(Action action)
|
||||
{
|
||||
steps.Add(new(new (new WaitForFixedUpdate()), action));
|
||||
steps.Add(new(YieldInstructionWrapper.Make(new WaitForFixedUpdate()), action));
|
||||
return this;
|
||||
}
|
||||
public ActionStepCoroutineWrapper Next(Action action)
|
||||
{
|
||||
steps.Add(new(new(new WaitForEndOfFrame()), action));
|
||||
steps.Add(new(YieldInstructionWrapper.Make(new WaitForEndOfFrame()), action));
|
||||
return this;
|
||||
}
|
||||
public ActionStepCoroutineWrapper Until(Func<bool> pr, Action action)
|
||||
{
|
||||
steps.Add(new(new(new WaitUntil(pr)), action));
|
||||
steps.Add(new(YieldInstructionWrapper.Make(new WaitUntil(pr)), action));
|
||||
return this;
|
||||
}
|
||||
private static IEnumerator Execute(List<KeyValuePair<YieldInstructionWrapper, Action>> steps)
|
||||
{
|
||||
foreach (var (waiting, action) in steps)
|
||||
{
|
||||
action();
|
||||
if (waiting.UnityYieldInstruction != null)
|
||||
yield return waiting.UnityYieldInstruction;
|
||||
else
|
||||
yield return waiting.CustomYieldInstruction;
|
||||
action();
|
||||
}
|
||||
}
|
||||
~ActionStepCoroutineWrapper()
|
||||
@@ -2199,6 +2266,39 @@ namespace Convention
|
||||
public static class ScriptingDefineUtility
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
public static void Add(string define, NamedBuildTarget target, bool log = false)
|
||||
{
|
||||
string definesString = PlayerSettings.GetScriptingDefineSymbols(target);
|
||||
if (definesString.Contains(define)) return;
|
||||
string[] allDefines = definesString.Split(';');
|
||||
ArrayUtility.Add(ref allDefines, define);
|
||||
definesString = string.Join(";", allDefines);
|
||||
PlayerSettings.SetScriptingDefineSymbols(target, definesString);
|
||||
Debug.Log("Added \"" + define + "\" from " + EditorUserBuildSettings.selectedBuildTargetGroup + " Scripting define in Player Settings");
|
||||
}
|
||||
|
||||
public static void Remove(string define, NamedBuildTarget target, bool log = false)
|
||||
{
|
||||
string definesString = PlayerSettings.GetScriptingDefineSymbols(target);
|
||||
if (!definesString.Contains(define)) return;
|
||||
string[] allDefines = definesString.Split(';');
|
||||
ArrayUtility.Remove(ref allDefines, define);
|
||||
definesString = string.Join(";", allDefines);
|
||||
PlayerSettings.SetScriptingDefineSymbols(target, definesString);
|
||||
Debug.Log("Removed \"" + define + "\" from " + EditorUserBuildSettings.selectedBuildTargetGroup + " Scripting define in Player Settings");
|
||||
}
|
||||
public static void Add(string define, BuildTargetGroup target, bool log = false)
|
||||
{
|
||||
Add(define, NamedBuildTarget.FromBuildTargetGroup(target), log);
|
||||
}
|
||||
|
||||
public static void Remove(string define, BuildTargetGroup target, bool log = false)
|
||||
{
|
||||
Remove(define, NamedBuildTarget.FromBuildTargetGroup(target), log);
|
||||
}
|
||||
#else
|
||||
public static void Add(string define, BuildTargetGroup target, bool log = false)
|
||||
{
|
||||
string definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(target);
|
||||
@@ -2220,6 +2320,8 @@ namespace Convention
|
||||
PlayerSettings.SetScriptingDefineSymbolsForGroup(target, definesString);
|
||||
Debug.Log("Removed \"" + define + "\" from " + EditorUserBuildSettings.selectedBuildTargetGroup + " Scripting define in Player Settings");
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -2444,33 +2546,6 @@ namespace Convention
|
||||
}
|
||||
}
|
||||
|
||||
namespace Convention
|
||||
{
|
||||
public static partial class ConventionUtility
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Unity engine function to disable the GC
|
||||
/// </summary>
|
||||
public static void GC_disable()
|
||||
{
|
||||
#if !UNITY_EDITOR
|
||||
GarbageCollector.GCMode = GarbageCollector.Mode.Disabled;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unity engine function to enable the GC
|
||||
/// </summary>
|
||||
public static void GC_enable()
|
||||
{
|
||||
#if !UNITY_EDITOR
|
||||
GarbageCollector.GCMode = GarbageCollector.Mode.Enabled;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Convention
|
||||
{
|
||||
public static partial class Utility
|
||||
@@ -2526,3 +2601,441 @@ namespace Convention
|
||||
}
|
||||
}
|
||||
|
||||
namespace Convention
|
||||
{
|
||||
public static class BinarySerializeUtility
|
||||
{
|
||||
#region Base
|
||||
|
||||
public static void WriteBool(BinaryWriter writer, bool data)
|
||||
{
|
||||
writer.Write(data);
|
||||
}
|
||||
public static bool ReadBool(BinaryReader reader)
|
||||
{
|
||||
return reader.ReadBoolean();
|
||||
}
|
||||
public static void WriteInt(BinaryWriter writer, in int data)
|
||||
{
|
||||
writer.Write(data);
|
||||
}
|
||||
public static int ReadInt(BinaryReader reader)
|
||||
{
|
||||
return reader.ReadInt32();
|
||||
}
|
||||
public static void WriteFloat(BinaryWriter writer, in float data)
|
||||
{
|
||||
writer.Write(data);
|
||||
}
|
||||
public static float ReadFloat(BinaryReader reader)
|
||||
{
|
||||
return reader.ReadSingle();
|
||||
}
|
||||
public static void WriteChar(BinaryWriter writer, in char data)
|
||||
{
|
||||
writer.Write(data);
|
||||
}
|
||||
public static char ReadChar(BinaryReader reader)
|
||||
{
|
||||
return reader.ReadChar();
|
||||
}
|
||||
public static void WriteString(BinaryWriter writer, in string data)
|
||||
{
|
||||
writer.Write(data ?? "");
|
||||
}
|
||||
public static string ReadString(BinaryReader reader)
|
||||
{
|
||||
return reader.ReadString();
|
||||
}
|
||||
public static void WriteVec3(BinaryWriter writer, in Vector3 data)
|
||||
{
|
||||
writer.Write(data.x);
|
||||
writer.Write(data.y);
|
||||
writer.Write(data.z);
|
||||
}
|
||||
public static Vector3 ReadVec3(BinaryReader reader)
|
||||
{
|
||||
float x, y, z;
|
||||
x = reader.ReadSingle();
|
||||
y = reader.ReadSingle();
|
||||
z = reader.ReadSingle();
|
||||
return new Vector3(x, y, z);
|
||||
}
|
||||
public static void WriteVec2(BinaryWriter writer, in Vector2 data)
|
||||
{
|
||||
writer.Write(data.x);
|
||||
writer.Write(data.y);
|
||||
}
|
||||
public static Vector2 ReadVec2(BinaryReader reader)
|
||||
{
|
||||
float x, y;
|
||||
x = reader.ReadSingle();
|
||||
y = reader.ReadSingle();
|
||||
return new Vector2(x, y);
|
||||
}
|
||||
public static void WriteColor(BinaryWriter writer, in Color data)
|
||||
{
|
||||
writer.Write(data.r);
|
||||
writer.Write(data.g);
|
||||
writer.Write(data.b);
|
||||
writer.Write(data.a);
|
||||
}
|
||||
public static Color ReadColor(BinaryReader reader)
|
||||
{
|
||||
float r, g, b, a;
|
||||
r = reader.ReadSingle();
|
||||
g = reader.ReadSingle();
|
||||
b = reader.ReadSingle();
|
||||
a = reader.ReadSingle();
|
||||
return new(r, g, b, a);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Serialize
|
||||
|
||||
#region Int
|
||||
|
||||
public static void SerializeNativeArray(BinaryWriter writer, in NativeArray<int> array, int start = 0, int end = int.MaxValue)
|
||||
{
|
||||
int e = Mathf.Min(array.Length, end);
|
||||
writer.Write(e - start);
|
||||
while (start < e)
|
||||
{
|
||||
writer.Write(array[start++]);
|
||||
}
|
||||
}
|
||||
public static int DeserializeNativeArray(BinaryReader reader, ref NativeArray<int> array)
|
||||
{
|
||||
int count = reader.ReadInt32();
|
||||
if (array.Length < count)
|
||||
array.ResizeArray(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
array[i] = reader.ReadInt32();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
public static void SerializeArray(BinaryWriter writer, in int[] array, int start = 0, int end = int.MaxValue)
|
||||
{
|
||||
int e = Mathf.Min(array.Length, end);
|
||||
writer.Write(e - start);
|
||||
while (start < e)
|
||||
{
|
||||
writer.Write(array[start++]);
|
||||
}
|
||||
}
|
||||
public static int[] DeserializeIntArray(BinaryReader reader)
|
||||
{
|
||||
int count = reader.ReadInt32();
|
||||
int[] array = new int[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
array[i] = reader.ReadInt32();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Float
|
||||
|
||||
public static void SerializeNativeArray(BinaryWriter writer, in NativeArray<float> array, int start = 0, int end = int.MaxValue)
|
||||
{
|
||||
int e = Mathf.Min(array.Length, end);
|
||||
writer.Write(e - start);
|
||||
while (start < e)
|
||||
{
|
||||
writer.Write(array[start++]);
|
||||
}
|
||||
}
|
||||
public static int DeserializeNativeArray(BinaryReader reader, ref NativeArray<float> array)
|
||||
{
|
||||
int count = reader.ReadInt32();
|
||||
if (array.Length < count)
|
||||
array.ResizeArray(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
array[i] = reader.ReadSingle();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
public static void SerializeArray(BinaryWriter writer, in float[] array, int start = 0, int end = int.MaxValue)
|
||||
{
|
||||
int e = Mathf.Min(array.Length, end);
|
||||
writer.Write(e - start);
|
||||
while (start < e)
|
||||
{
|
||||
writer.Write(array[start++]);
|
||||
}
|
||||
}
|
||||
public static float[] DeserializeFloatArray(BinaryReader reader)
|
||||
{
|
||||
int count = reader.ReadInt32();
|
||||
float[] array = new float[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
array[i] = reader.ReadSingle();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region String
|
||||
|
||||
public static void SerializeArray(BinaryWriter writer, in string[] array, int start = 0, int end = int.MaxValue)
|
||||
{
|
||||
int e = Mathf.Min(array.Length, end);
|
||||
writer.Write(e - start);
|
||||
while (start < e)
|
||||
{
|
||||
writer.Write(array[start++] ?? "");
|
||||
}
|
||||
}
|
||||
public static string[] DeserializeStringArray(BinaryReader reader)
|
||||
{
|
||||
int count = reader.ReadInt32();
|
||||
string[] array = new string[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
array[i] = reader.ReadString();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Vector3
|
||||
|
||||
public static void SerializeNativeArray(BinaryWriter writer, in NativeArray<Vector3> array, int start = 0, int end = int.MaxValue)
|
||||
{
|
||||
int e = Mathf.Min(array.Length, end);
|
||||
writer.Write(e - start);
|
||||
while (start < e)
|
||||
{
|
||||
writer.Write(array[start].x);
|
||||
writer.Write(array[start].y);
|
||||
writer.Write(array[start].z);
|
||||
start++;
|
||||
}
|
||||
}
|
||||
public static int DeserializeNativeArray(BinaryReader reader, ref NativeArray<Vector3> array)
|
||||
{
|
||||
int count = reader.ReadInt32();
|
||||
if (array.Length < count)
|
||||
array.ResizeArray(count);
|
||||
float x, y, z;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
x = reader.ReadSingle();
|
||||
y = reader.ReadSingle();
|
||||
z = reader.ReadSingle();
|
||||
array[i] = new(x, y, z);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
public static void SerializeArray(BinaryWriter writer, in Vector3[] array, int start = 0, int end = int.MaxValue)
|
||||
{
|
||||
int e = Mathf.Min(array.Length, end);
|
||||
writer.Write(e - start);
|
||||
while (start < e)
|
||||
{
|
||||
writer.Write(array[start].x);
|
||||
writer.Write(array[start].y);
|
||||
writer.Write(array[start].z);
|
||||
start++;
|
||||
}
|
||||
}
|
||||
public static Vector3[] DeserializeVec3Array(BinaryReader reader, int start = 0)
|
||||
{
|
||||
int count = reader.ReadInt32();
|
||||
Vector3[] array = new Vector3[count];
|
||||
float x, y, z;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
x = reader.ReadSingle();
|
||||
y = reader.ReadSingle();
|
||||
z = reader.ReadSingle();
|
||||
array[start + i] = new(x, y, z);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Vector2
|
||||
|
||||
public static void SerializeNativeArray(BinaryWriter writer, in NativeArray<Vector2> array, int start = 0, int end = int.MaxValue)
|
||||
{
|
||||
int e = Mathf.Min(array.Length, end);
|
||||
writer.Write(e - start);
|
||||
while (start < e)
|
||||
{
|
||||
writer.Write(array[start].x);
|
||||
writer.Write(array[start].y);
|
||||
start++;
|
||||
}
|
||||
}
|
||||
public static int DeserializeNativeArray(BinaryReader reader, ref NativeArray<Vector2> array)
|
||||
{
|
||||
int count = reader.ReadInt32();
|
||||
if (array.Length < count)
|
||||
array.ResizeArray(count);
|
||||
float x, y;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
x = reader.ReadSingle();
|
||||
y = reader.ReadSingle();
|
||||
array[i] = new(x, y);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
public static void SerializeArray(BinaryWriter writer, in Vector2[] array, int start = 0, int end = int.MaxValue)
|
||||
{
|
||||
int e = Mathf.Min(array.Length, end);
|
||||
writer.Write(e - start);
|
||||
while (start < e)
|
||||
{
|
||||
writer.Write(array[start].x);
|
||||
writer.Write(array[start].y);
|
||||
start++;
|
||||
}
|
||||
}
|
||||
public static Vector2[] DeserializeVec2Array(BinaryReader reader)
|
||||
{
|
||||
int count = reader.ReadInt32();
|
||||
Vector2[] array = new Vector2[count];
|
||||
float x, y;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
x = reader.ReadSingle();
|
||||
y = reader.ReadSingle();
|
||||
array[i] = new(x, y);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Color
|
||||
|
||||
public static void SerializeNativeArray(BinaryWriter writer, in NativeArray<Color> array, int start = 0, int end = int.MaxValue)
|
||||
{
|
||||
int e = Mathf.Min(array.Length, end);
|
||||
writer.Write(e - start);
|
||||
while (start < e)
|
||||
{
|
||||
WriteColor(writer, array[start]);
|
||||
start++;
|
||||
}
|
||||
}
|
||||
public static int DeserializeNativeArray(BinaryReader reader, ref NativeArray<Color> array)
|
||||
{
|
||||
int count = reader.ReadInt32();
|
||||
if (array.Length < count)
|
||||
array.ResizeArray(count);
|
||||
float x, y, z;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
array[i] = ReadColor(reader);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
public static void SerializeArray(BinaryWriter writer, in Color[] array, int start = 0, int end = int.MaxValue)
|
||||
{
|
||||
int e = Mathf.Min(array.Length, end);
|
||||
writer.Write(e - start);
|
||||
while (start < e)
|
||||
{
|
||||
WriteColor(writer, array[start]);
|
||||
start++;
|
||||
}
|
||||
}
|
||||
public static Color[] DeserializeVec3Array(BinaryReader reader)
|
||||
{
|
||||
int count = reader.ReadInt32();
|
||||
Color[] array = new Color[count];
|
||||
float x, y, z;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
array[i] = ReadColor(reader);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static partial class Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取类型的友好显示名称,支持泛型、数组等复杂类型
|
||||
/// </summary>
|
||||
public static string GetFriendlyName(this Type type)
|
||||
{
|
||||
if (type == null) return null;
|
||||
|
||||
// 处理泛型类型
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
// 特殊处理可空类型(Nullable<T>)
|
||||
if (type.IsNullable())
|
||||
{
|
||||
return $"{GetFriendlyName(type.GetGenericArguments()[0])}?";
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
// 获取类型基础名称(移除 `1, `2 等后缀)
|
||||
string baseName = type.Name.Contains('`') ? type.Name[..type.Name.IndexOf('`')] : type.Name;
|
||||
sb.Append(baseName);
|
||||
sb.Append('<');
|
||||
|
||||
// 递归处理泛型参数
|
||||
Type[] args = type.GetGenericArguments();
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (i > 0) sb.Append(", ");
|
||||
sb.Append(GetFriendlyName(args[i]));
|
||||
}
|
||||
|
||||
sb.Append('>');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
// 处理数组类型
|
||||
if (type.IsArray)
|
||||
{
|
||||
string elementName = GetFriendlyName(type.GetElementType());
|
||||
int rank = type.GetArrayRank();
|
||||
return rank == 1 ? $"{elementName}[]" : $"{elementName}[{new string(',', rank - 1)}]";
|
||||
}
|
||||
|
||||
// 处理指针类型
|
||||
if (type.IsPointer)
|
||||
{
|
||||
return $"{GetFriendlyName(type.GetElementType())}*";
|
||||
}
|
||||
|
||||
// 处理引用类型(ref/out 参数)
|
||||
if (type.IsByRef)
|
||||
{
|
||||
return $"{GetFriendlyName(type.GetElementType())}&";
|
||||
}
|
||||
|
||||
// 普通类型直接返回名称
|
||||
return type.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断是否为可空类型
|
||||
/// </summary>
|
||||
public static bool IsNullable(this Type type)
|
||||
{
|
||||
return type.IsGenericType &&
|
||||
type.GetGenericTypeDefinition() == typeof(Nullable<>);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Convention.Collections
|
||||
{
|
||||
@@ -453,4 +454,985 @@ namespace Convention.Collections
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Native LRUCache - <20><><EFBFBD><EFBFBD>Unity.Collections<6E>ĸ<EFBFBD><C4B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD>û<EFBFBD><C3BB><EFBFBD>
|
||||
/// Burst<73><74><EFBFBD>ݣ<EFBFBD><DDA3><EFBFBD>Ҫ<EFBFBD>ֶ<EFBFBD>Dispose
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"><3E><><EFBFBD><EFBFBD><EFBFBD>ͣ<EFBFBD><CDA3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>unmanaged<65><64><EFBFBD>ͣ<EFBFBD></typeparam>
|
||||
/// <typeparam name="TValue">ֵ<><D6B5><EFBFBD>ͣ<EFBFBD><CDA3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>unmanaged<65><64><EFBFBD>ͣ<EFBFBD></typeparam>
|
||||
public struct NativeLRUCache<TKey, TValue> : IDisposable
|
||||
where TKey : unmanaged, IEquatable<TKey>
|
||||
where TValue : unmanaged
|
||||
{
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD>ڵ<EFBFBD>
|
||||
private struct Node
|
||||
{
|
||||
public TKey key;
|
||||
public TValue value;
|
||||
public int prevIndex; // ǰһ<C7B0><D2BB><EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD><EFBFBD><EFBFBD>
|
||||
public int nextIndex; // <20><>һ<EFBFBD><D2BB><EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD><EFBFBD><EFBFBD>
|
||||
}
|
||||
|
||||
private NativeList<Node> nodes; // <20>ڵ<EFBFBD><DAB5><EFBFBD>
|
||||
private readonly NativeHashMap<TKey, int> hashMap; // key -> <20>ڵ<EFBFBD><DAB5><EFBFBD><EFBFBD><EFBFBD>
|
||||
private readonly NativeList<int> freeList; // <20><><EFBFBD>нڵ<D0BD><DAB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
|
||||
private readonly int capacity;
|
||||
private int headIndex; // <20><><EFBFBD><EFBFBD>ͷ<EFBFBD><CDB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>δʹ<CEB4>ã<EFBFBD>
|
||||
private int tailIndex; // <20><><EFBFBD><EFBFBD>β<EFBFBD><CEB2><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD>ã<EFBFBD>
|
||||
private int count;
|
||||
|
||||
// <20>Ƿ<EFBFBD>Ϊ<EFBFBD><CEAA>Ч<EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD><EFBFBD><EFBFBD>
|
||||
private readonly bool IsValidIndex(int index) => index >= 0;
|
||||
|
||||
public NativeLRUCache(int capacity, Allocator allocator)
|
||||
{
|
||||
this.capacity = capacity;
|
||||
this.count = 0;
|
||||
this.headIndex = -1;
|
||||
this.tailIndex = -1;
|
||||
|
||||
hashMap = new NativeHashMap<TKey, int>(capacity, allocator);
|
||||
nodes = new NativeList<Node>(capacity, allocator);
|
||||
freeList = new NativeList<int>(capacity, allocator);
|
||||
|
||||
// Ԥ<><D4A4><EFBFBD><EFBFBD><EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD>
|
||||
nodes.Length = capacity;
|
||||
for (int i = capacity - 1; i >= 0; i--)
|
||||
{
|
||||
freeList.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>ֵ
|
||||
/// </summary>
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
if (hashMap.TryGetValue(key, out int nodeIndex))
|
||||
{
|
||||
MoveToTail(nodeIndex); // <20>ƶ<EFBFBD><C6B6><EFBFBD>β<EFBFBD><CEB2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD>ã<EFBFBD>
|
||||
value = nodes[nodeIndex].value;
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD>ӻ<EFBFBD><D3BB><EFBFBD><EFBFBD>»<EFBFBD><C2BB><EFBFBD>
|
||||
/// </summary>
|
||||
public void Put(TKey key, TValue value)
|
||||
{
|
||||
// <20>Ѵ<EFBFBD><D1B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>²<EFBFBD><C2B2>ƶ<EFBFBD><C6B6><EFBFBD>β<EFBFBD><CEB2>
|
||||
if (hashMap.TryGetValue(key, out int nodeIndex))
|
||||
{
|
||||
Node node = nodes[nodeIndex];
|
||||
node.value = value;
|
||||
nodes[nodeIndex] = node;
|
||||
MoveToTail(nodeIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̭ͷ<CCAD><CDB7><EFBFBD>ڵ<EFBFBD>
|
||||
if (count >= capacity)
|
||||
{
|
||||
EvictHead();
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD>½ڵ<C2BD>
|
||||
nodeIndex = AllocateNode();
|
||||
nodes[nodeIndex] = new Node
|
||||
{
|
||||
key = key,
|
||||
value = value,
|
||||
prevIndex = tailIndex,
|
||||
nextIndex = -1
|
||||
};
|
||||
|
||||
hashMap.Add(key, nodeIndex);
|
||||
AddToTail(nodeIndex);
|
||||
count++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ɾ<><C9BE><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
if (!hashMap.TryGetValue(key, out int nodeIndex))
|
||||
return false;
|
||||
|
||||
hashMap.Remove(key);
|
||||
RemoveFromList(nodeIndex);
|
||||
FreeNode(nodeIndex);
|
||||
count--;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD>ջ<EFBFBD><D5BB><EFBFBD>
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
hashMap.Clear();
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>нڵ㵽<DAB5><E3B5BD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD>
|
||||
freeList.Clear();
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
freeList.Add(i);
|
||||
}
|
||||
|
||||
headIndex = -1;
|
||||
tailIndex = -1;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD><EFBFBD><EFBFBD>
|
||||
private readonly int AllocateNode()
|
||||
{
|
||||
int index = freeList[^1];
|
||||
freeList.RemoveAtSwapBack(freeList.Length - 1);
|
||||
return index;
|
||||
}
|
||||
|
||||
// <20>ͷŽڵ<C5BD><DAB5><EFBFBD><EFBFBD><EFBFBD>
|
||||
private readonly void FreeNode(int index)
|
||||
{
|
||||
freeList.Add(index);
|
||||
}
|
||||
|
||||
// <20><><EFBFBD>ڵ<EFBFBD><DAB5>ƶ<EFBFBD><C6B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>β<EFBFBD><CEB2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD>ʹ<EFBFBD>ã<EFBFBD>
|
||||
private void MoveToTail(int nodeIndex)
|
||||
{
|
||||
if (nodeIndex == tailIndex)
|
||||
return;
|
||||
|
||||
RemoveFromList(nodeIndex);
|
||||
AddToTail(nodeIndex);
|
||||
}
|
||||
|
||||
// <20><><EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD><EFBFBD>ӵ<EFBFBD>β<EFBFBD><CEB2>
|
||||
private void AddToTail(int nodeIndex)
|
||||
{
|
||||
Node node = nodes[nodeIndex];
|
||||
node.prevIndex = tailIndex;
|
||||
node.nextIndex = -1;
|
||||
nodes[nodeIndex] = node;
|
||||
|
||||
if (IsValidIndex(tailIndex))
|
||||
{
|
||||
Node tail = nodes[tailIndex];
|
||||
tail.nextIndex = nodeIndex;
|
||||
nodes[tailIndex] = tail;
|
||||
}
|
||||
|
||||
tailIndex = nodeIndex;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD>գ<EFBFBD>ͷβָ<CEB2><D6B8>ͬһ<CDAC>ڵ<EFBFBD>
|
||||
if (!IsValidIndex(headIndex))
|
||||
{
|
||||
headIndex = nodeIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƴ<EFBFBD><C6B3>ڵ<EFBFBD>
|
||||
private void RemoveFromList(int nodeIndex)
|
||||
{
|
||||
Node node = nodes[nodeIndex];
|
||||
|
||||
// <20><><EFBFBD><EFBFBD>ǰһ<C7B0><D2BB><EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD>nextָ<74><D6B8>
|
||||
if (IsValidIndex(node.prevIndex))
|
||||
{
|
||||
Node prev = nodes[node.prevIndex];
|
||||
prev.nextIndex = node.nextIndex;
|
||||
nodes[node.prevIndex] = prev;
|
||||
}
|
||||
else
|
||||
{
|
||||
// û<><C3BB>ǰһ<C7B0><D2BB><EFBFBD>ڵ㣬˵<E3A3AC><CBB5><EFBFBD><EFBFBD>ͷ<EFBFBD>ڵ<EFBFBD>
|
||||
headIndex = node.nextIndex;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD>º<EFBFBD>һ<EFBFBD><D2BB><EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD>prevָ<76><D6B8>
|
||||
if (IsValidIndex(node.nextIndex))
|
||||
{
|
||||
Node next = nodes[node.nextIndex];
|
||||
next.prevIndex = node.prevIndex;
|
||||
nodes[node.nextIndex] = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// û<>к<EFBFBD>һ<EFBFBD><D2BB><EFBFBD>ڵ㣬˵<E3A3AC><CBB5><EFBFBD><EFBFBD>β<EFBFBD>ڵ<EFBFBD>
|
||||
tailIndex = node.prevIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// <20><>̭ͷ<CCAD><CDB7><EFBFBD>ڵ㣨<DAB5><E3A3A8><EFBFBD><EFBFBD>δʹ<CEB4>ã<EFBFBD>
|
||||
private void EvictHead()
|
||||
{
|
||||
if (!IsValidIndex(headIndex)) return;
|
||||
|
||||
int oldHead = headIndex;
|
||||
Node headNode = nodes[oldHead];
|
||||
|
||||
hashMap.Remove(headNode.key);
|
||||
headIndex = headNode.nextIndex;
|
||||
|
||||
if (IsValidIndex(headIndex))
|
||||
{
|
||||
Node newHead = nodes[headIndex];
|
||||
newHead.prevIndex = -1;
|
||||
nodes[headIndex] = newHead;
|
||||
}
|
||||
else
|
||||
{
|
||||
tailIndex = -1; // <20><><EFBFBD><EFBFBD>Ϊ<EFBFBD><CEAA>
|
||||
}
|
||||
|
||||
FreeNode(oldHead);
|
||||
count--;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (hashMap.IsCreated) hashMap.Dispose();
|
||||
if (nodes.IsCreated) nodes.Dispose();
|
||||
if (freeList.IsCreated) freeList.Dispose();
|
||||
|
||||
headIndex = -1;
|
||||
tailIndex = -1;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD>Է<EFBFBD><D4B7><EFBFBD><EFBFBD><EFBFBD>
|
||||
public readonly int Count => count;
|
||||
public readonly int Capacity => capacity;
|
||||
public readonly bool IsCreated => hashMap.IsCreated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Native LFUCache - <20><><EFBFBD><EFBFBD>Unity.Collections<6E>ĸ<EFBFBD><C4B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƶ<EEB2BB><C6B5>ʹ<EFBFBD>û<EFBFBD><C3BB><EFBFBD>
|
||||
/// Burst<73><74><EFBFBD>ݣ<EFBFBD><DDA3><EFBFBD>Ҫ<EFBFBD>ֶ<EFBFBD>Dispose
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"><3E><><EFBFBD><EFBFBD><EFBFBD>ͣ<EFBFBD><CDA3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>unmanaged<65><64><EFBFBD>ͣ<EFBFBD></typeparam>
|
||||
/// <typeparam name="TValue">ֵ<><D6B5><EFBFBD>ͣ<EFBFBD><CDA3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>unmanaged<65><64><EFBFBD>ͣ<EFBFBD></typeparam>
|
||||
public struct NativeLFUCache<TKey, TValue> : IDisposable
|
||||
where TKey : unmanaged, IEquatable<TKey>
|
||||
where TValue : unmanaged
|
||||
{
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD>ڵ<EFBFBD>
|
||||
private struct Node
|
||||
{
|
||||
public TKey key;
|
||||
public TValue value;
|
||||
public int frequency; // <20><><EFBFBD><EFBFBD>Ƶ<EFBFBD><C6B5>
|
||||
public int prevIndex; // ͬƵ<CDAC><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>е<EFBFBD>ǰһ<C7B0><D2BB><EFBFBD>ڵ<EFBFBD>
|
||||
public int nextIndex; // ͬƵ<CDAC><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>еĺ<D0B5>һ<EFBFBD><D2BB><EFBFBD>ڵ<EFBFBD>
|
||||
}
|
||||
|
||||
// Ƶ<><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͷβ
|
||||
private struct FrequencyList
|
||||
{
|
||||
public int headIndex; // <20><>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͷ<EFBFBD>ڵ㣨<DAB5><E3A3A8><EFBFBD><EFBFBD>δʹ<CEB4>ã<EFBFBD>
|
||||
public int tailIndex; // <20><>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>β<EFBFBD>ڵ㣨<DAB5><E3A3A8><EFBFBD><EFBFBD>ʹ<EFBFBD>ã<EFBFBD>
|
||||
|
||||
public readonly bool IsEmpty => headIndex == -1;
|
||||
}
|
||||
|
||||
private readonly NativeHashMap<TKey, int> keyToNodeIndex; // key -> nodeIndex
|
||||
private NativeList<Node> nodes; // <20>ڵ<EFBFBD><DAB5><EFBFBD>
|
||||
private readonly NativeList<int> freeList; // <20><><EFBFBD>нڵ<D0BD><DAB5><EFBFBD><EFBFBD><EFBFBD>
|
||||
private NativeHashMap<int, FrequencyList> freqToList; // frequency -> FrequencyList
|
||||
|
||||
private readonly int capacity;
|
||||
private int count;
|
||||
private int minFrequency; // <20><>ǰ<EFBFBD><C7B0>СƵ<D0A1><C6B5>
|
||||
|
||||
public NativeLFUCache(int capacity, Allocator allocator)
|
||||
{
|
||||
this.capacity = capacity;
|
||||
this.count = 0;
|
||||
this.minFrequency = 0;
|
||||
|
||||
keyToNodeIndex = new NativeHashMap<TKey, int>(capacity, allocator);
|
||||
nodes = new NativeList<Node>(capacity, allocator);
|
||||
freeList = new NativeList<int>(capacity, allocator);
|
||||
freqToList = new NativeHashMap<int, FrequencyList>(capacity, allocator);
|
||||
|
||||
// Ԥ<><D4A4><EFBFBD><EFBFBD><EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD>
|
||||
nodes.Length = capacity;
|
||||
for (int i = capacity - 1; i >= 0; i--)
|
||||
{
|
||||
freeList.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƶ<EFBFBD><C6B5>
|
||||
/// </summary>
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
if (keyToNodeIndex.TryGetValue(key, out int nodeIndex))
|
||||
{
|
||||
// <20><><EFBFBD>ӷ<EFBFBD><D3B7><EFBFBD>Ƶ<EFBFBD><C6B5>
|
||||
IncreaseFrequency(nodeIndex);
|
||||
|
||||
value = nodes[nodeIndex].value;
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD>ӻ<EFBFBD><D3BB><EFBFBD><EFBFBD>»<EFBFBD><C2BB><EFBFBD>
|
||||
/// </summary>
|
||||
public void Put(TKey key, TValue value)
|
||||
{
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ
|
||||
if (keyToNodeIndex.TryGetValue(key, out int nodeIndex))
|
||||
{
|
||||
Node node = nodes[nodeIndex];
|
||||
node.value = value;
|
||||
nodes[nodeIndex] = node;
|
||||
|
||||
IncreaseFrequency(nodeIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̭<EFBFBD><CCAD>СƵ<D0A1>ʵĽڵ<C4BD>
|
||||
if (count >= capacity)
|
||||
{
|
||||
EvictMinFrequencyNode();
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD>½ڵ㣬<DAB5><E3A3AC>ʼƵ<CABC><C6B5>Ϊ1
|
||||
nodeIndex = AllocateNode();
|
||||
nodes[nodeIndex] = new Node
|
||||
{
|
||||
key = key,
|
||||
value = value,
|
||||
frequency = 1,
|
||||
prevIndex = -1,
|
||||
nextIndex = -1
|
||||
};
|
||||
|
||||
keyToNodeIndex.Add(key, nodeIndex);
|
||||
AddToFrequencyList(nodeIndex, 1);
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>СƵ<D0A1><C6B5>
|
||||
if (minFrequency == 0)
|
||||
{
|
||||
minFrequency = 1;
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ɾ<><C9BE><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
if (!keyToNodeIndex.TryGetValue(key, out int nodeIndex))
|
||||
return false;
|
||||
|
||||
RemoveNode(nodeIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD>ջ<EFBFBD><D5BB><EFBFBD>
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
keyToNodeIndex.Clear();
|
||||
freqToList.Clear();
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>нڵ㵽<DAB5><E3B5BD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD>
|
||||
freeList.Clear();
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
freeList.Add(i);
|
||||
}
|
||||
|
||||
minFrequency = 0;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20>ͷ<EFBFBD><CDB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڴ<EFBFBD>
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (keyToNodeIndex.IsCreated) keyToNodeIndex.Dispose();
|
||||
if (nodes.IsCreated) nodes.Dispose();
|
||||
if (freeList.IsCreated) freeList.Dispose();
|
||||
if (freqToList.IsCreated) freqToList.Dispose();
|
||||
|
||||
minFrequency = 0;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
#region <EFBFBD>ڲ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
|
||||
private int AllocateNode()
|
||||
{
|
||||
int index = freeList[^1];
|
||||
freeList.RemoveAtSwapBack(freeList.Length - 1);
|
||||
return index;
|
||||
}
|
||||
|
||||
private void FreeNode(int index)
|
||||
{
|
||||
freeList.Add(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD>ӽڵ<D3BD><DAB5>ķ<EFBFBD><C4B7><EFBFBD>Ƶ<EFBFBD><C6B5>
|
||||
/// </summary>
|
||||
private void IncreaseFrequency(int nodeIndex)
|
||||
{
|
||||
Node node = nodes[nodeIndex];
|
||||
int oldFreq = node.frequency;
|
||||
int newFreq = oldFreq + 1;
|
||||
|
||||
// <20>Ӿ<EFBFBD>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƴ<EFBFBD>
|
||||
RemoveFromFrequencyList(nodeIndex, oldFreq);
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD>Ƿ<EFBFBD><C7B7><EFBFBD>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD>СƵ<D0A1><C6B5>
|
||||
if (oldFreq == minFrequency && GetFrequencyListHead(oldFreq) == -1)
|
||||
{
|
||||
minFrequency = newFreq;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD>½ڵ<C2BD>Ƶ<EFBFBD><C6B5>
|
||||
node.frequency = newFreq;
|
||||
nodes[nodeIndex] = node;
|
||||
|
||||
// <20><><EFBFBD>ӵ<EFBFBD><D3B5><EFBFBD>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
AddToFrequencyList(nodeIndex, newFreq);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20><>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƴ<EFBFBD><C6B3>ڵ<EFBFBD>
|
||||
/// </summary>
|
||||
private void RemoveFromFrequencyList(int nodeIndex, int frequency)
|
||||
{
|
||||
if (!freqToList.TryGetValue(frequency, out var list))
|
||||
return;
|
||||
|
||||
Node node = nodes[nodeIndex];
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͷ<EFBFBD>ڵ<EFBFBD>
|
||||
if (node.prevIndex == -1)
|
||||
{
|
||||
list.headIndex = node.nextIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// <20><><EFBFBD><EFBFBD>ǰһ<C7B0><D2BB><EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD>nextָ<74><D6B8>
|
||||
Node prevNode = nodes[node.prevIndex];
|
||||
prevNode.nextIndex = node.nextIndex;
|
||||
nodes[node.prevIndex] = prevNode;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>β<EFBFBD>ڵ<EFBFBD>
|
||||
if (node.nextIndex == -1)
|
||||
{
|
||||
list.tailIndex = node.prevIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// <20><><EFBFBD>º<EFBFBD>һ<EFBFBD><D2BB><EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD>prevָ<76><D6B8>
|
||||
Node nextNode = nodes[node.nextIndex];
|
||||
nextNode.prevIndex = node.prevIndex;
|
||||
nodes[node.nextIndex] = nextNode;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>գ<EFBFBD><D5A3>Ƴ<EFBFBD><C6B3><EFBFBD>Ƶ<EFBFBD><C6B5>
|
||||
if (list.headIndex == -1)
|
||||
{
|
||||
freqToList.Remove(frequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
freqToList[frequency] = list;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD>ڵ<EFBFBD><DAB5><EFBFBD><EFBFBD>ӵ<EFBFBD>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>β<EFBFBD><CEB2>
|
||||
/// </summary>
|
||||
private void AddToFrequencyList(int nodeIndex, int frequency)
|
||||
{
|
||||
if (!freqToList.TryGetValue(frequency, out var list))
|
||||
{
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD>µ<EFBFBD>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
list = new FrequencyList { headIndex = nodeIndex, tailIndex = nodeIndex };
|
||||
freqToList.Add(frequency, list);
|
||||
|
||||
// <20><><EFBFBD>ýڵ<C3BD>ָ<EFBFBD><D6B8>
|
||||
Node node = nodes[nodeIndex];
|
||||
node.prevIndex = -1;
|
||||
node.nextIndex = -1;
|
||||
nodes[nodeIndex] = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
// <20><><EFBFBD>ӵ<EFBFBD><D3B5><EFBFBD><EFBFBD><EFBFBD>β<EFBFBD><CEB2>
|
||||
int oldTail = list.tailIndex;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD>ԭβ<D4AD>ڵ<EFBFBD>
|
||||
Node oldTailNode = nodes[oldTail];
|
||||
oldTailNode.nextIndex = nodeIndex;
|
||||
nodes[oldTail] = oldTailNode;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD>½ڵ<C2BD>
|
||||
Node newNode = nodes[nodeIndex];
|
||||
newNode.prevIndex = oldTail;
|
||||
newNode.nextIndex = -1;
|
||||
nodes[nodeIndex] = newNode;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>β
|
||||
list.tailIndex = nodeIndex;
|
||||
freqToList[frequency] = list;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20><>ȡƵ<C8A1><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͷ<EFBFBD>ڵ<EFBFBD>
|
||||
/// </summary>
|
||||
private int GetFrequencyListHead(int frequency)
|
||||
{
|
||||
if (freqToList.TryGetValue(frequency, out var list))
|
||||
{
|
||||
return list.headIndex;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20><>̭<EFBFBD><CCAD>СƵ<D0A1>ʵĽڵ㣨<DAB5><E3A3A8>̭<EFBFBD><CCAD>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͷ<EFBFBD>ڵ㣩
|
||||
/// </summary>
|
||||
private void EvictMinFrequencyNode()
|
||||
{
|
||||
// <20>ҵ<EFBFBD><D2B5>нڵ<D0BD><DAB5><EFBFBD><EFBFBD><EFBFBD>СƵ<D0A1><C6B5>
|
||||
while (minFrequency <= capacity && GetFrequencyListHead(minFrequency) == -1)
|
||||
{
|
||||
minFrequency++;
|
||||
}
|
||||
|
||||
if (minFrequency > capacity) return;
|
||||
|
||||
int headIndex = GetFrequencyListHead(minFrequency);
|
||||
if (headIndex == -1) return;
|
||||
|
||||
RemoveNode(headIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20>Ƴ<EFBFBD><C6B3><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڵ㣨<DAB5><E3A3A8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݽṹ<DDBD><E1B9B9>ɾ<EFBFBD><C9BE><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
private void RemoveNode(int nodeIndex)
|
||||
{
|
||||
Node node = nodes[nodeIndex];
|
||||
|
||||
// <20><>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƴ<EFBFBD>
|
||||
RemoveFromFrequencyList(nodeIndex, node.frequency);
|
||||
|
||||
// <20><><EFBFBD><EFBFBD>HashMap<61>Ƴ<EFBFBD>
|
||||
keyToNodeIndex.Remove(node.key);
|
||||
|
||||
// <20><><EFBFBD>սڵ<D5BD>
|
||||
FreeNode(nodeIndex);
|
||||
count--;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// <20><><EFBFBD>Է<EFBFBD><D4B7><EFBFBD><EFBFBD><EFBFBD>
|
||||
public readonly int Count => count;
|
||||
public readonly int Capacity => capacity;
|
||||
public readonly bool IsCreated => keyToNodeIndex.IsCreated;
|
||||
}
|
||||
|
||||
public struct NativeARCCache<TKey, TValue> : IDisposable
|
||||
where TKey : unmanaged, IEquatable<TKey>
|
||||
where TValue : unmanaged
|
||||
{
|
||||
private const int LIST_T1 = 0, LIST_T2 = 1, LIST_B1 = 2, LIST_B2 = 3;
|
||||
|
||||
private struct Node
|
||||
{
|
||||
public TKey key;
|
||||
public TValue value;
|
||||
public byte listType;
|
||||
public int prevIndex;
|
||||
public int nextIndex;
|
||||
}
|
||||
|
||||
private NativeHashMap<TKey, int> keyToNode;
|
||||
private NativeList<Node> nodes;
|
||||
private NativeList<int> freeList;
|
||||
|
||||
// <20>ĸ<EFBFBD><C4B8><EFBFBD><EFBFBD><EFBFBD>ͷβָ<CEB2><D6B8>
|
||||
private int t1Head, t1Tail, t2Head, t2Tail, b1Head, b1Tail, b2Head, b2Tail;
|
||||
|
||||
private int capacity, t1Count, t2Count, b1Count, b2Count, p;
|
||||
|
||||
public NativeARCCache(int capacity, Allocator allocator)
|
||||
{
|
||||
this.capacity = capacity;
|
||||
this.t1Count = this.t2Count = this.b1Count = this.b2Count = 0;
|
||||
this.p = 0;
|
||||
|
||||
t1Head = t1Tail = t2Head = t2Tail = -1;
|
||||
b1Head = b1Tail = b2Head = b2Tail = -1;
|
||||
|
||||
int totalNodes = capacity * 2;
|
||||
keyToNode = new NativeHashMap<TKey, int>(totalNodes, allocator);
|
||||
nodes = new NativeList<Node>(totalNodes, allocator);
|
||||
freeList = new NativeList<int>(totalNodes, allocator);
|
||||
|
||||
nodes.Length = totalNodes;
|
||||
for (int i = totalNodes - 1; i >= 0; i--)
|
||||
{
|
||||
freeList.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
if (keyToNode.TryGetValue(key, out int nodeIndex))
|
||||
{
|
||||
Node node = nodes[nodeIndex]; // <20>ȶ<EFBFBD>ȡ
|
||||
|
||||
if (node.listType == LIST_T1 || node.listType == LIST_T2)
|
||||
{
|
||||
MoveToMRU(nodeIndex, LIST_T2);
|
||||
value = node.value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (node.listType == LIST_B1)
|
||||
{
|
||||
node = ComputeNewPAndReplace(node, b2Count, b1Count, true);
|
||||
b1Count--; t2Count++;
|
||||
value = node.value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (node.listType == LIST_B2)
|
||||
{
|
||||
node = ComputeNewPAndReplace(node, b1Count, b2Count, false);
|
||||
b2Count--; t2Count++;
|
||||
value = node.value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Put(TKey key, TValue value)
|
||||
{
|
||||
if (keyToNode.TryGetValue(key, out int nodeIndex))
|
||||
{
|
||||
Node node = nodes[nodeIndex];
|
||||
node.value = value;
|
||||
|
||||
if (node.listType == LIST_T1 || node.listType == LIST_T2)
|
||||
{
|
||||
MoveToMRU(nodeIndex, LIST_T2);
|
||||
}
|
||||
else if (node.listType == LIST_B1)
|
||||
{
|
||||
node = ComputeNewPAndReplace(node, b2Count, b1Count, true);
|
||||
b1Count--; t2Count++;
|
||||
}
|
||||
else if (node.listType == LIST_B2)
|
||||
{
|
||||
node = ComputeNewPAndReplace(node, b1Count, b2Count, false);
|
||||
b2Count--; t2Count++;
|
||||
}
|
||||
|
||||
nodes[nodeIndex] = node; // д<><D0B4>
|
||||
return;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD><D6B4>Replace
|
||||
int totalSize = t1Count + t2Count;
|
||||
if (totalSize >= capacity)
|
||||
{
|
||||
Replace();
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD>½ڵ㵽T1
|
||||
nodeIndex = AllocateNode();
|
||||
nodes[nodeIndex] = new Node
|
||||
{
|
||||
key = key,
|
||||
value = value,
|
||||
listType = LIST_T1,
|
||||
prevIndex = -1,
|
||||
nextIndex = -1
|
||||
};
|
||||
|
||||
keyToNode.Add(key, nodeIndex);
|
||||
AddToHead(nodeIndex, LIST_T1);
|
||||
t1Count++;
|
||||
}
|
||||
|
||||
private Node ComputeNewPAndReplace(Node node, int oppositeCount, int sameCount, bool isB1)
|
||||
{
|
||||
int delta = (oppositeCount >= sameCount) ? 1 : oppositeCount / sameCount + 1;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD>pֵ
|
||||
if (isB1) p = Math.Min(p + delta, capacity);
|
||||
else p = Math.Max(p - delta, 0);
|
||||
|
||||
// ִ<><D6B4><EFBFBD>滻
|
||||
Replace();
|
||||
|
||||
// <20>ƶ<EFBFBD><C6B6>ڵ<EFBFBD>
|
||||
MoveBetweenLists(node.prevIndex, node.listType, LIST_T2);
|
||||
node.listType = LIST_T2;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private void Replace()
|
||||
{
|
||||
if (t1Count > 0 && (t1Count > p || (b2Count > 0 && t1Count == p)))
|
||||
{
|
||||
MoveBetweenLists(t1Head, LIST_T1, LIST_B1);
|
||||
t1Count--; b1Count++;
|
||||
|
||||
if (b1Count > capacity)
|
||||
{
|
||||
int old = b1Head; Node n = nodes[old];
|
||||
RemoveFromList(old);
|
||||
keyToNode.Remove(n.key);
|
||||
FreeNode(old);
|
||||
b1Count--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveBetweenLists(t2Head, LIST_T2, LIST_B2);
|
||||
t2Count--; b2Count++;
|
||||
|
||||
if (b2Count > capacity)
|
||||
{
|
||||
int old = b2Head; Node n = nodes[old];
|
||||
RemoveFromList(old);
|
||||
keyToNode.Remove(n.key);
|
||||
FreeNode(old);
|
||||
b2Count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
|
||||
private void MoveToMRU(int nodeIndex, byte targetList)
|
||||
{
|
||||
RemoveFromList(nodeIndex);
|
||||
AddToHead(nodeIndex, targetList);
|
||||
}
|
||||
|
||||
private void MoveBetweenLists(int nodeIndex, byte fromList, byte toList)
|
||||
{
|
||||
RemoveFromList(nodeIndex);
|
||||
AddToHead(nodeIndex, toList);
|
||||
}
|
||||
|
||||
private void AddToHead(int nodeIndex, byte listType)
|
||||
{
|
||||
Node node = nodes[nodeIndex]; // <20><>ȡ
|
||||
node.listType = listType;
|
||||
node.prevIndex = -1;
|
||||
|
||||
switch (listType)
|
||||
{
|
||||
case LIST_T1:
|
||||
node.nextIndex = t1Head;
|
||||
nodes[nodeIndex] = node; // д<><D0B4>
|
||||
if (t1Head != -1)
|
||||
{
|
||||
Node headNode = nodes[t1Head];
|
||||
headNode.prevIndex = nodeIndex;
|
||||
nodes[t1Head] = headNode; // д<><D0B4>
|
||||
}
|
||||
t1Head = nodeIndex;
|
||||
if (t1Tail == -1) t1Tail = nodeIndex;
|
||||
break;
|
||||
|
||||
case LIST_T2:
|
||||
node.nextIndex = t2Head;
|
||||
nodes[nodeIndex] = node; // д<><D0B4>
|
||||
if (t2Head != -1)
|
||||
{
|
||||
Node headNode = nodes[t2Head];
|
||||
headNode.prevIndex = nodeIndex;
|
||||
nodes[t2Head] = headNode; // д<><D0B4>
|
||||
}
|
||||
t2Head = nodeIndex;
|
||||
if (t2Tail == -1) t2Tail = nodeIndex;
|
||||
break;
|
||||
|
||||
case LIST_B1:
|
||||
node.nextIndex = b1Head;
|
||||
nodes[nodeIndex] = node; // д<><D0B4>
|
||||
if (b1Head != -1)
|
||||
{
|
||||
Node headNode = nodes[b1Head];
|
||||
headNode.prevIndex = nodeIndex;
|
||||
nodes[b1Head] = headNode; // д<><D0B4>
|
||||
}
|
||||
b1Head = nodeIndex;
|
||||
if (b1Tail == -1) b1Tail = nodeIndex;
|
||||
break;
|
||||
|
||||
case LIST_B2:
|
||||
node.nextIndex = b2Head;
|
||||
nodes[nodeIndex] = node; // д<><D0B4>
|
||||
if (b2Head != -1)
|
||||
{
|
||||
Node headNode = nodes[b2Head];
|
||||
headNode.prevIndex = nodeIndex;
|
||||
nodes[b2Head] = headNode; // д<><D0B4>
|
||||
}
|
||||
b2Head = nodeIndex;
|
||||
if (b2Tail == -1) b2Tail = nodeIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveFromList(int nodeIndex)
|
||||
{
|
||||
Node node = nodes[nodeIndex]; // <20><>ȡ
|
||||
int prev = node.prevIndex;
|
||||
int next = node.nextIndex;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><CDB8><EFBFBD>ͷβָ<CEB2><D6B8>
|
||||
switch (node.listType)
|
||||
{
|
||||
case LIST_T1:
|
||||
if (nodeIndex == t1Head) t1Head = next;
|
||||
if (nodeIndex == t1Tail) t1Tail = prev;
|
||||
break;
|
||||
case LIST_T2:
|
||||
if (nodeIndex == t2Head) t2Head = next;
|
||||
if (nodeIndex == t2Tail) t2Tail = prev;
|
||||
break;
|
||||
case LIST_B1:
|
||||
if (nodeIndex == b1Head) b1Head = next;
|
||||
if (nodeIndex == b1Tail) b1Tail = prev;
|
||||
break;
|
||||
case LIST_B2:
|
||||
if (nodeIndex == b2Head) b2Head = next;
|
||||
if (nodeIndex == b2Tail) b2Tail = prev;
|
||||
break;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD>ھ<EFBFBD>
|
||||
if (prev != -1)
|
||||
{
|
||||
Node prevNode = nodes[prev];
|
||||
prevNode.nextIndex = next;
|
||||
nodes[prev] = prevNode; // д<><D0B4>
|
||||
}
|
||||
if (next != -1)
|
||||
{
|
||||
Node nextNode = nodes[next];
|
||||
nextNode.prevIndex = prev;
|
||||
nodes[next] = nextNode; // д<><D0B4>
|
||||
}
|
||||
|
||||
node.prevIndex = node.nextIndex = -1;
|
||||
nodes[nodeIndex] = node; // д<><D0B4>
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private int AllocateNode()
|
||||
{
|
||||
int index = freeList[^1];
|
||||
freeList.RemoveAtSwapBack(freeList.Length - 1);
|
||||
return index;
|
||||
}
|
||||
|
||||
private void FreeNode(int index)
|
||||
{
|
||||
freeList.Add(index);
|
||||
}
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
if (!keyToNode.TryGetValue(key, out int nodeIndex))
|
||||
return false;
|
||||
|
||||
Node node = nodes[nodeIndex]; // <20><>ȡ
|
||||
|
||||
// <20><><EFBFBD>¼<EFBFBD><C2BC><EFBFBD>
|
||||
if (node.listType == LIST_T1) t1Count--;
|
||||
else if (node.listType == LIST_T2) t2Count--;
|
||||
else if (node.listType == LIST_B1) b1Count--;
|
||||
else if (node.listType == LIST_B2) b2Count--;
|
||||
|
||||
RemoveFromList(nodeIndex);
|
||||
keyToNode.Remove(key);
|
||||
FreeNode(nodeIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
keyToNode.Clear();
|
||||
|
||||
freeList.Clear();
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
freeList.Add(i);
|
||||
}
|
||||
|
||||
t1Head = t1Tail = t2Head = t2Tail = -1;
|
||||
b1Head = b1Tail = b2Head = b2Tail = -1;
|
||||
|
||||
t1Count = t2Count = b1Count = b2Count = 0;
|
||||
p = 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (keyToNode.IsCreated) keyToNode.Dispose();
|
||||
if (nodes.IsCreated) nodes.Dispose();
|
||||
if (freeList.IsCreated) freeList.Dispose();
|
||||
|
||||
t1Head = t1Tail = t2Head = t2Tail = -1;
|
||||
b1Head = b1Tail = b2Head = b2Tail = -1;
|
||||
|
||||
t1Count = t2Count = b1Count = b2Count = 0;
|
||||
p = 0;
|
||||
}
|
||||
|
||||
public int Count => t1Count + t2Count;
|
||||
public int Capacity => capacity;
|
||||
public bool IsCreated => keyToNode.IsCreated;
|
||||
public int T1Count => t1Count;
|
||||
public int T2Count => t2Count;
|
||||
public int PValue => p;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,18 +201,7 @@ namespace Convention
|
||||
{
|
||||
if (IsFile() == false)
|
||||
throw new InvalidOperationException("Target is not a file");
|
||||
var file = this.OriginInfo as FileInfo;
|
||||
const int BlockSize = 1024;
|
||||
long FileSize = file.Length;
|
||||
byte[] result = new byte[FileSize];
|
||||
long offset = 0;
|
||||
using (var fs = file.OpenRead())
|
||||
{
|
||||
fs.ReadAsync(result[(int)(offset)..(int)(offset + BlockSize)], 0, (int)(offset + BlockSize) - (int)(offset));
|
||||
offset += BlockSize;
|
||||
offset = System.Math.Min(offset, FileSize);
|
||||
}
|
||||
return result;
|
||||
return File.ReadAllBytes(OriginPath);
|
||||
}
|
||||
|
||||
public List<string[]> LoadAsCsv()
|
||||
@@ -340,7 +329,8 @@ namespace Convention
|
||||
|
||||
public void SaveAsBinary(byte[] data)
|
||||
{
|
||||
SaveDataAsBinary(OriginPath, data, (OriginInfo as FileInfo).OpenWrite());
|
||||
using var fs = (OriginInfo as FileInfo).OpenWrite();
|
||||
SaveDataAsBinary(OriginPath, data, fs);
|
||||
}
|
||||
|
||||
public void SaveAsCsv(List<string[]> csvData)
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Burst;
|
||||
using UnityEngine;
|
||||
#if UNITY_URP
|
||||
using static Unity.Mathematics.math;
|
||||
#endif
|
||||
using static Convention.MathExtension;
|
||||
|
||||
namespace Convention
|
||||
{
|
||||
[BurstCompile]
|
||||
public static partial class MathExtension
|
||||
{
|
||||
#region EaseCurve
|
||||
@@ -105,6 +106,7 @@ namespace Convention
|
||||
};
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float Linear(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -112,6 +114,7 @@ namespace Convention
|
||||
return c * t / 1f + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InQuad(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -119,6 +122,7 @@ namespace Convention
|
||||
return c * t * t + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float OutQuad(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -126,6 +130,7 @@ namespace Convention
|
||||
return -c * t * (t - 2f) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InOutQuad(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -135,6 +140,7 @@ namespace Convention
|
||||
return -c / 2f * (t * (t - 2) - 1) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InCubic(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -142,6 +148,7 @@ namespace Convention
|
||||
return c * t * t * t + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float OutCubic(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -150,6 +157,7 @@ namespace Convention
|
||||
return c * (t * t * t + 1) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InOutCubic(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -159,6 +167,7 @@ namespace Convention
|
||||
return c / 2f * (t * t * t + 2) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InQuart(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -166,6 +175,7 @@ namespace Convention
|
||||
return c * t * t * t * t + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float OutQuart(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -174,6 +184,7 @@ namespace Convention
|
||||
return -c * (t * t * t * t - 1) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InOutQuart(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -183,6 +194,7 @@ namespace Convention
|
||||
return -c / 2f * (t * t * t * t - 2) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InQuint(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -190,6 +202,7 @@ namespace Convention
|
||||
return c * t * t * t * t * t + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float OutQuint(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -198,6 +211,7 @@ namespace Convention
|
||||
return c * (t * t * t * t * t + 1) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InOutQuint(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -207,36 +221,42 @@ namespace Convention
|
||||
return c / 2f * (t * t * t * t * t + 2) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InSine(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
return -c * Mathf.Cos(t / 1f * (Mathf.PI / 2f)) + c + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float OutSine(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
return c * Mathf.Sin(t / 1f * (Mathf.PI / 2f)) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InOutSine(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
return -c / 2f * (Mathf.Cos(Mathf.PI * t / 1f) - 1) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InExpo(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
return c * Mathf.Pow(2, 10 * (t / 1f - 1)) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float OutExpo(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
return c * (-Mathf.Pow(2, -10 * t / 1f) + 1) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InOutExpo(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -246,6 +266,7 @@ namespace Convention
|
||||
return c / 2f * (-Mathf.Pow(2, -10 * t) + 2) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InCirc(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -253,6 +274,7 @@ namespace Convention
|
||||
return -c * (Mathf.Sqrt(1 - t * t) - 1) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float OutCirc(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -261,6 +283,7 @@ namespace Convention
|
||||
return c * Mathf.Sqrt(1 - t * t) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InOutCirc(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -270,12 +293,14 @@ namespace Convention
|
||||
return c / 2f * (Mathf.Sqrt(1 - t * t) + 1) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InBounce(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
return c - OutBounce(0f, c, 1f - t) + from; //does this work?
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float OutBounce(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -298,6 +323,7 @@ namespace Convention
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InOutBounce(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -306,6 +332,7 @@ namespace Convention
|
||||
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InElastic(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -316,6 +343,7 @@ namespace Convention
|
||||
return -(c * Mathf.Pow(2, 10 * (t -= 1)) * Mathf.Sin((t - s) * (2 * Mathf.PI) / p)) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float OutElastic(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -326,6 +354,7 @@ namespace Convention
|
||||
return (c * Mathf.Pow(2, -10 * t) * Mathf.Sin((t - s) * (2 * Mathf.PI) / p) + c + from);
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InOutElastic(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -338,6 +367,7 @@ namespace Convention
|
||||
return c * Mathf.Pow(2, -10 * (t -= 1)) * Mathf.Sin((t - s) * (2f * Mathf.PI) / p) * 0.5f + c + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InBack(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -346,6 +376,7 @@ namespace Convention
|
||||
return c * t * t * ((s + 1) * t - s) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float OutBack(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
@@ -354,6 +385,7 @@ namespace Convention
|
||||
return c * (t * t * ((s + 1) * t + s) + 1) + from;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public static float InOutBack(float from, float to, float t)
|
||||
{
|
||||
float c = to - from;
|
||||
|
||||
@@ -844,28 +844,26 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 72ece51f2901e7445ab60da3685d6b5f, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_ShowDebugText: 0
|
||||
m_ShowCameraFrustum: 1
|
||||
m_IgnoreTimeScale: 0
|
||||
m_WorldUpOverride: {fileID: 0}
|
||||
m_UpdateMethod: 2
|
||||
m_BlendUpdateMethod: 1
|
||||
m_DefaultBlend:
|
||||
m_Style: 1
|
||||
m_Time: 0.5
|
||||
m_CustomCurve:
|
||||
ShowDebugText: 0
|
||||
ShowCameraFrustum: 1
|
||||
IgnoreTimeScale: 0
|
||||
WorldUpOverride: {fileID: 0}
|
||||
ChannelMask: -1
|
||||
UpdateMethod: 2
|
||||
BlendUpdateMethod: 1
|
||||
LensModeOverride:
|
||||
Enabled: 0
|
||||
DefaultMode: 2
|
||||
DefaultBlend:
|
||||
Style: 1
|
||||
Time: 0.5
|
||||
CustomCurve:
|
||||
serializedVersion: 2
|
||||
m_Curve: []
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
m_CustomBlends: {fileID: 0}
|
||||
m_CameraCutEvent:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_CameraActivatedEvent:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
CustomBlends: {fileID: 0}
|
||||
--- !u!114 &1937889226080204937
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1060,12 +1058,13 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 45e653bab7fb20e499bda25e1b646fea, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_ExcludedPropertiesInInspector:
|
||||
- m_Script
|
||||
m_LockStageInInspector:
|
||||
Priority:
|
||||
Enabled: 0
|
||||
m_Value: 0
|
||||
OutputChannel: 1
|
||||
StandbyUpdate: 2
|
||||
m_StreamingVersion: 20170927
|
||||
m_Priority: 10
|
||||
m_StandbyUpdate: 2
|
||||
m_LegacyPriority: 10
|
||||
m_LookAt: {fileID: 0}
|
||||
m_Follow: {fileID: 0}
|
||||
m_Lens:
|
||||
@@ -1075,17 +1074,30 @@ MonoBehaviour:
|
||||
FarClipPlane: 5000
|
||||
Dutch: 0
|
||||
ModeOverride: 0
|
||||
LensShift: {x: 0, y: 0}
|
||||
GateFit: 2
|
||||
FocusDistance: 10
|
||||
m_SensorSize: {x: 1, y: 1}
|
||||
m_Transitions:
|
||||
LensShift: {x: 0, y: 0}
|
||||
FocusDistance: 10
|
||||
Iso: 200
|
||||
ShutterSpeed: 0.005
|
||||
Aperture: 16
|
||||
BladeCount: 5
|
||||
Curvature: {x: 2, y: 11}
|
||||
BarrelClipping: 0.25
|
||||
Anamorphism: 0
|
||||
BlendHint: 0
|
||||
m_OnCameraLiveEvent:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_ExcludedPropertiesInInspector:
|
||||
- m_Script
|
||||
m_LockStageInInspector:
|
||||
m_LegacyTransitions:
|
||||
m_BlendHint: 0
|
||||
m_InheritPosition: 0
|
||||
m_OnCameraLive:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_LegacyBlendHint: 0
|
||||
m_ComponentOwner: {fileID: 11535700043902880}
|
||||
--- !u!20 &2319537278398014183
|
||||
Camera:
|
||||
@@ -1439,12 +1451,13 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 45e653bab7fb20e499bda25e1b646fea, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_ExcludedPropertiesInInspector:
|
||||
- m_Script
|
||||
m_LockStageInInspector:
|
||||
Priority:
|
||||
Enabled: 0
|
||||
m_Value: 0
|
||||
OutputChannel: 1
|
||||
StandbyUpdate: 2
|
||||
m_StreamingVersion: 20170927
|
||||
m_Priority: 10
|
||||
m_StandbyUpdate: 2
|
||||
m_LegacyPriority: 10
|
||||
m_LookAt: {fileID: 0}
|
||||
m_Follow: {fileID: 0}
|
||||
m_Lens:
|
||||
@@ -1454,17 +1467,30 @@ MonoBehaviour:
|
||||
FarClipPlane: 5000
|
||||
Dutch: 0
|
||||
ModeOverride: 0
|
||||
LensShift: {x: 0, y: 0}
|
||||
GateFit: 2
|
||||
FocusDistance: 10
|
||||
m_SensorSize: {x: 1, y: 1}
|
||||
m_Transitions:
|
||||
LensShift: {x: 0, y: 0}
|
||||
FocusDistance: 10
|
||||
Iso: 200
|
||||
ShutterSpeed: 0.005
|
||||
Aperture: 16
|
||||
BladeCount: 5
|
||||
Curvature: {x: 2, y: 11}
|
||||
BarrelClipping: 0.25
|
||||
Anamorphism: 0
|
||||
BlendHint: 0
|
||||
m_OnCameraLiveEvent:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_ExcludedPropertiesInInspector:
|
||||
- m_Script
|
||||
m_LockStageInInspector:
|
||||
m_LegacyTransitions:
|
||||
m_BlendHint: 0
|
||||
m_InheritPosition: 0
|
||||
m_OnCameraLive:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_LegacyBlendHint: 0
|
||||
m_ComponentOwner: {fileID: 3000479872335485243}
|
||||
--- !u!20 &7206794941111705965
|
||||
Camera:
|
||||
|
||||
Reference in New Issue
Block a user