提交示例代码

This commit is contained in:
Bob.Song
2026-03-05 11:39:06 +08:00
commit 25958f58c3
2534 changed files with 209593 additions and 0 deletions

Binary file not shown.

View File

@@ -0,0 +1,64 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

View File

@@ -0,0 +1,35 @@
# Specify filepatterns you want git to ignore.
obj/
Bin/
bin/
Temp/
Library/
Logs/
temp/
DocFx/
global.json
# 项目素材
ResLibrary/
# Unity项目文件
examples/**/Unity/*.sln
examples/**/Unity/*.csproj
examples/**/Unity/.vsconfig
# Client-Unity
HybridCLRData/
AssetBundles/
UserSettings/
# 忽略Packages.Unity包下的.meta
Packages.Unity/**/*.meta
# Other
.idea
.vs
.vscode
*.DS_Store
# 不排除示例项目的.vscode
!examples/**/.vscode
!Tools/**/.vscode
examples/**/Unity/.vscode

View File

@@ -0,0 +1,83 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuGet", "NuGet", "{83FB151F-5881-4757-9505-8B70B8C38AA3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fantasy.Config", "Fantasy.Packages\Fantasy.Config\Fantasy.Config.csproj", "{BAB58713-49A4-4F34-AE69-333AA1F19DC9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fantasy.ConfigTable", "Fantasy.Packages\Fantasy.ConfigTable\Net\Fantasy.ConfigTable.csproj", "{B7383D00-BCF9-4F1B-A79A-6C11DB324237}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fantasy.NLog", "Fantasy.Packages\Fantasy.NLog\Fantasy.NLog.csproj", "{138EF56B-7154-4656-B55C-30429A68DCC8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fantasy.Tools.ExporterConfigTable", "Tools\NuGet\Fantasy.Tools.ExporterConfigTable\Fantasy.Tools.ExporterConfigTable.csproj", "{0BC5E44D-A515-4776-B42D-4872E40194B7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fantasy.Tools.ExporterNetworkProtocol", "Tools\NuGet\Fantasy.Tools.ExporterNetworkProtocol\Fantasy.Tools.ExporterNetworkProtocol.csproj", "{5D780C1D-0505-4CF0-925C-1373DCFCBA58}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fantasy.Net", "Fantasy.Net\Fantasy.Net\Fantasy.Net.csproj", "{4782A7D1-2B43-48DD-AF50-EDC773436E20}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fantasy.Console", "Fantays.Console\Fantasy.Console.csproj", "{4E463740-00D2-4B4F-AC5B-A09B24487D35}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{0CEB6A4D-8CE9-4EA7-A918-61FA62D6F6F0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fantasy.Tools.ConfigTable", "Tools\SourceCode\Fantasy.Tools.ConfigTable\Fantasy.Tools.ConfigTable.csproj", "{A93DFC08-E57F-4BBF-BAAA-18FDB458D515}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fantasy.Tools.NetworkProtocol", "Tools\SourceCode\Fantasy.Tools.NetworkProtocol\Fantasy.Tools.NetworkProtocol.csproj", "{1951BC8A-0B3E-42F8-950B-DC142988B225}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fantasy.Benchmark", "Fantasy.Benchmark\Fantasy.Benchmark.csproj", "{C228C5CC-81B9-4323-B4BD-F3C1FB129676}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{BAB58713-49A4-4F34-AE69-333AA1F19DC9} = {83FB151F-5881-4757-9505-8B70B8C38AA3}
{B7383D00-BCF9-4F1B-A79A-6C11DB324237} = {83FB151F-5881-4757-9505-8B70B8C38AA3}
{138EF56B-7154-4656-B55C-30429A68DCC8} = {83FB151F-5881-4757-9505-8B70B8C38AA3}
{0BC5E44D-A515-4776-B42D-4872E40194B7} = {83FB151F-5881-4757-9505-8B70B8C38AA3}
{5D780C1D-0505-4CF0-925C-1373DCFCBA58} = {83FB151F-5881-4757-9505-8B70B8C38AA3}
{A93DFC08-E57F-4BBF-BAAA-18FDB458D515} = {0CEB6A4D-8CE9-4EA7-A918-61FA62D6F6F0}
{1951BC8A-0B3E-42F8-950B-DC142988B225} = {0CEB6A4D-8CE9-4EA7-A918-61FA62D6F6F0}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{BAB58713-49A4-4F34-AE69-333AA1F19DC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BAB58713-49A4-4F34-AE69-333AA1F19DC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BAB58713-49A4-4F34-AE69-333AA1F19DC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BAB58713-49A4-4F34-AE69-333AA1F19DC9}.Release|Any CPU.Build.0 = Release|Any CPU
{B7383D00-BCF9-4F1B-A79A-6C11DB324237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7383D00-BCF9-4F1B-A79A-6C11DB324237}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7383D00-BCF9-4F1B-A79A-6C11DB324237}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7383D00-BCF9-4F1B-A79A-6C11DB324237}.Release|Any CPU.Build.0 = Release|Any CPU
{138EF56B-7154-4656-B55C-30429A68DCC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{138EF56B-7154-4656-B55C-30429A68DCC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{138EF56B-7154-4656-B55C-30429A68DCC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{138EF56B-7154-4656-B55C-30429A68DCC8}.Release|Any CPU.Build.0 = Release|Any CPU
{0BC5E44D-A515-4776-B42D-4872E40194B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0BC5E44D-A515-4776-B42D-4872E40194B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0BC5E44D-A515-4776-B42D-4872E40194B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0BC5E44D-A515-4776-B42D-4872E40194B7}.Release|Any CPU.Build.0 = Release|Any CPU
{5D780C1D-0505-4CF0-925C-1373DCFCBA58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5D780C1D-0505-4CF0-925C-1373DCFCBA58}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5D780C1D-0505-4CF0-925C-1373DCFCBA58}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5D780C1D-0505-4CF0-925C-1373DCFCBA58}.Release|Any CPU.Build.0 = Release|Any CPU
{4782A7D1-2B43-48DD-AF50-EDC773436E20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4782A7D1-2B43-48DD-AF50-EDC773436E20}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4782A7D1-2B43-48DD-AF50-EDC773436E20}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4782A7D1-2B43-48DD-AF50-EDC773436E20}.Release|Any CPU.Build.0 = Release|Any CPU
{4E463740-00D2-4B4F-AC5B-A09B24487D35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4E463740-00D2-4B4F-AC5B-A09B24487D35}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E463740-00D2-4B4F-AC5B-A09B24487D35}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4E463740-00D2-4B4F-AC5B-A09B24487D35}.Release|Any CPU.Build.0 = Release|Any CPU
{A93DFC08-E57F-4BBF-BAAA-18FDB458D515}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A93DFC08-E57F-4BBF-BAAA-18FDB458D515}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A93DFC08-E57F-4BBF-BAAA-18FDB458D515}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A93DFC08-E57F-4BBF-BAAA-18FDB458D515}.Release|Any CPU.Build.0 = Release|Any CPU
{1951BC8A-0B3E-42F8-950B-DC142988B225}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1951BC8A-0B3E-42F8-950B-DC142988B225}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1951BC8A-0B3E-42F8-950B-DC142988B225}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1951BC8A-0B3E-42F8-950B-DC142988B225}.Release|Any CPU.Build.0 = Release|Any CPU
{C228C5CC-81B9-4323-B4BD-F3C1FB129676}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C228C5CC-81B9-4323-B4BD-F3C1FB129676}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C228C5CC-81B9-4323-B4BD-F3C1FB129676}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C228C5CC-81B9-4323-B4BD-F3C1FB129676}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,4 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AConsoleKeyInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F2470bd56a5084219a8a1bf41b31f8b7c35000_003F38_003Fe3b96ef8_003FConsoleKeyInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADictionary_00602_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F77fc0eb92b774686bbae91cb92331703d83600_003Feb_003F3472606c_003FDictionary_00602_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AKCPClientNetwork_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F345510aa2adc4dc692470d34421b7bf380400_003F47_003F946135ef_003FKCPClientNetwork_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>

View File

@@ -0,0 +1,16 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fantasy.Net", "Fantasy.Net\Fantasy.Net.csproj", "{636FBF87-A6D2-4A31-86FF-F4157F558C95}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{636FBF87-A6D2-4A31-86FF-F4157F558C95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{636FBF87-A6D2-4A31-86FF-F4157F558C95}.Debug|Any CPU.Build.0 = Debug|Any CPU
{636FBF87-A6D2-4A31-86FF-F4157F558C95}.Release|Any CPU.ActiveCfg = Release|Any CPU
{636FBF87-A6D2-4A31-86FF-F4157F558C95}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,34 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- &lt;!&ndash; 物理复制 Excel 配置文件到项目根目录 &ndash;&gt;-->
<!-- <Target Name="CopyExcelFilesToProject" BeforeTargets="PrepareForBuild">-->
<!-- <ItemGroup>-->
<!-- <ExcelFilesToCopy Include="$(MSBuildThisFileDirectory)..\build\MachineConfig.xlsx" />-->
<!-- <ExcelFilesToCopy Include="$(MSBuildThisFileDirectory)..\build\SceneConfig.xlsx" />-->
<!-- <ExcelFilesToCopy Include="$(MSBuildThisFileDirectory)..\build\ProcessConfig.xlsx" />-->
<!-- <ExcelFilesToCopy Include="$(MSBuildThisFileDirectory)..\build\WorldConfig.xlsx" />-->
<!-- </ItemGroup>-->
<!-- <Copy SourceFiles="@(ExcelFilesToCopy)" DestinationFolder="$(MSBuildProjectDirectory)/ServerConfig" SkipUnchangedFiles="true" />-->
<!-- </Target>-->
<!-- &lt;!&ndash; 物理复制 NLog 配置文件到项目根目录 &ndash;&gt;-->
<!-- <Target Name="CopyNLogFilesToProject" BeforeTargets="PrepareForBuild">-->
<!-- <ItemGroup>-->
<!-- &lt;!&ndash; 定义源文件路径,指向 NuGet 包中的文件 &ndash;&gt;-->
<!-- <FilesToCopy Include="$(MSBuildThisFileDirectory)..\build\NLog.config" />-->
<!-- <FilesToCopy Include="$(MSBuildThisFileDirectory)..\build\NLog.xsd" />-->
<!-- </ItemGroup>-->
<!-- &lt;!&ndash; 使用 Copy 任务将文件复制到项目物理根目录 &ndash;&gt;-->
<!-- <Copy SourceFiles="@(FilesToCopy)" DestinationFolder="$(MSBuildProjectDirectory)" SkipUnchangedFiles="true" /> -->
<!-- &lt;!&ndash; 将复制的文件添加到解决方案中,并设置复制到输出目录 &ndash;&gt;-->
<!-- <ItemGroup>-->
<!-- &lt;!&ndash; 使用 Include 确保文件在解决方案中显示 &ndash;&gt;-->
<!-- <None Include="NLog.config">-->
<!-- &lt;!&ndash; 确保复制到输出目录,并设置复制模式 &ndash;&gt;-->
<!-- <CopyToOutputDirectory>Always</CopyToOutputDirectory>-->
<!-- </None>-->
<!-- <None Include="NLog.xsd">-->
<!-- &lt;!&ndash; 确保复制到输出目录,并设置复制模式 &ndash;&gt;-->
<!-- <CopyToOutputDirectory>Always</CopyToOutputDirectory>-->
<!-- </None>-->
<!-- </ItemGroup>-->
<!-- </Target>-->
</Project>

View File

@@ -0,0 +1,59 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Fantasy-Net</PackageId>
<PackageVersion>2024.2.24</PackageVersion>
<Title>Fantasy-Net</Title>
<Authors>qq362946</Authors>
<owners>qq362946</owners>
<PackageOutputPath>../../nupkg</PackageOutputPath>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<Description>
Fantasy is a high-performance network development framework based on .NET, supporting mainstream protocols. It is designed for development teams or individuals needing a quick start, scalability, and a distributed, cross-platform solution at the commercial level. Fantasy aims to provide easy-to-use tools while ensuring high system performance and scalability.</Description>
<Copyright>Copyright 2026 qq362946</Copyright>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageProjectUrl>https://www.code-fantasy.com/</PackageProjectUrl>
<RepositoryUrl>https://github.com/qq362946/Fantasy</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>Net, c#, Server, Game, GameServer, Fantasy , Network</PackageTags>
<PackageIcon>icon.png</PackageIcon>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyName>Fantasy-Net</AssemblyName>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DefineConstants>TRACE;FANTASY_NET</DefineConstants>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin\Debug\net8.0\Fantasy.Net.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DefineConstants>TRACE;FANTASY_NET</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<FrameworkReference Include="Microsoft.AspNetCore.App"/>
<PackageReference Include="MongoDB.Bson" Version="3.1.0" />
<PackageReference Include="MongoDB.Driver" Version="3.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="protobuf-net" Version="3.2.45" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="System.IO.Pipelines" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<None Include="Fantasy-Net.targets" Pack="true" PackagePath="buildTransitive" />
<None Include="icon.png" Pack="true" PackagePath="\"/>
<None Include="LICENSE" Pack="true" PackagePath="\"/>
<None Include="README.md" Pack="true" PackagePath="\"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,14 @@
MIT License
Copyright (c) 2023 qq362946
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
However, the following entity is explicitly prohibited from using, copying, modifying, or distributing the Software or any of its portions:
泰课在线https://www.taikr.com/
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,34 @@
# Fantasy
#### 框架支持TCP\KCP\WebSocket\http支持Unity发布成H5三种网络协议采用的Protobuf/MemoryPack/Bson做为消息的序列化方案。
#### Fantasy是基于.NET的高性能网络开发框架支持主流协议前后端分离适合需要快速上手、可扩展、分布式全平台商业级解决方案的开发团队或个人。它旨在提供易用工具同时保证系统的高性能和扩展性。
## 导航
* [Fantasy介绍网站](https://www.code-fantasy.com/)
* [Fantasy的API文档](https://www.code-fantasy.com/doc/api/Fantasy.html)
* [入门视频观看地址](https://space.bilibili.com/382126312)
## 快速上手
* 01.快速入门
* [1.1.获得Fantasy](https://www.code-fantasy.com/top/download-fantasy/)
* [1.2.安装Fantasy](https://www.code-fantasy.com/top/creating-your-app/)
* [1.3.Fantasy的网络](https://www.code-fantasy.com/top/use-network/)
* [1.4.Fantasy的配置文件](https://www.code-fantasy.com/top/config-file/)
* [1.5.Fantasy的命令行参数](https://www.code-fantasy.com/top/command-line-parameter/)
* [1.6.Fantasy的导表工具](https://www.code-fantasy.com/top/guidance/)
* [1.7.如何升级到最新版](https://www.code-fantasy.com/top/upgrade/)
* 02.网络通信
* [2.1.客户端服务器之间发送消息](https://www.code-fantasy.com/network/session/)
* [2.2.服务器之间发送消息](https://www.code-fantasy.com/network/networkmessagingomponent/)
* [2.3.定义通信协议](https://www.code-fantasy.com/network/network-protocols/)
* [2.4.Route通信协议](https://www.code-fantasy.com/network/network-route/)
* [2.5.Addressable通信协议](https://www.code-fantasy.com/network/network-addressable/)
* 03.系统组件
* [3.1.ECS系统](https://www.code-fantasy.com/core/ecs/)
* [3.2.事件系统](https://www.code-fantasy.com/core/event/)
* [3.3.任务系统](https://www.code-fantasy.com/core/task/)
* [3.4.异步协程锁](https://www.code-fantasy.com/core/lock/)
* [3.5.数据库](https://www.code-fantasy.com/core/db/)
* [更新日志](https://www.code-fantasy.com/changelog/)
* [API文档](https://www.code-fantasy.com/doc/api/Fantasy.html)
* [常见问题](https://www.code-fantasy.com/question/)
## 交流与讨论:
__讨论QQ群 : Fantasy服务器开发交流群 569888673 __

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,276 @@
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 ConcurrentBag<IAssembly> AssemblySystems = new ConcurrentBag<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;
}
AssemblySystems.Add(assemblySystem);
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
while (AssemblySystems.TryTake(out var removeAssemblySystem))
{
if (removeAssemblySystem == assemblySystem)
{
continue;
}
AssemblySystems.Add(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,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,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,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,205 @@
#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>
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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,21 @@
MIT License
Copyright (c) 2024 Nevin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,325 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if NET7_0_OR_GREATER
using System.Numerics;
using System.Runtime.Intrinsics;
#else
using System;
#endif
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// BitOperations helpers
/// </summary>
internal static class BitOperationsHelpers
{
#if !NET7_0_OR_GREATER
/// <summary>
/// DeBruijn sequence
/// </summary>
private static ReadOnlySpan<byte> Log2DeBruijn => new byte[32]
{
0, 9, 1, 10, 13, 21, 2, 29,
11, 14, 16, 18, 22, 25, 3, 30,
8, 12, 20, 28, 15, 17, 24, 7,
19, 27, 23, 6, 26, 5, 4, 31
};
#endif
/// <summary>
/// Log2
/// </summary>
/// <param name="value">Value</param>
/// <returns>Log2</returns>
public static int Log2(int value) => Log2((uint)value);
/// <summary>
/// Log2
/// </summary>
/// <param name="value">Value</param>
/// <returns>Log2</returns>
public static int Log2(uint value)
{
#if NET7_0_OR_GREATER
return BitOperations.Log2(value);
#else
value |= 1;
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
return Unsafe.AddByteOffset(ref MemoryMarshal.GetReference(Log2DeBruijn), (nint)(int)((value * 130329821U) >> 27));
#endif
}
/// <summary>
/// And
/// </summary>
/// <param name="destination">Destination</param>
/// <param name="source">Source</param>
/// <param name="count">Count</param>
public static void And(Span<int> destination, Span<int> source, uint count)
{
switch (count)
{
case 7:
destination[6] &= source[6];
goto case 6;
case 6:
destination[5] &= source[5];
goto case 5;
case 5:
destination[4] &= source[4];
goto case 4;
case 4:
destination[3] &= source[3];
goto case 3;
case 3:
destination[2] &= source[2];
goto case 2;
case 2:
destination[1] &= source[1];
goto case 1;
case 1:
destination[0] &= source[0];
return;
case 0:
return;
}
ref var left = ref MemoryMarshal.GetReference(destination);
ref var right = ref MemoryMarshal.GetReference(source);
#if NET7_0_OR_GREATER
uint i = 0;
if (Vector256.IsHardwareAccelerated)
{
var n = count - 7;
for (; i < n; i += 8)
{
var result = Vector256.LoadUnsafe(ref left, i) & Vector256.LoadUnsafe(ref right, i);
result.StoreUnsafe(ref left, i);
}
}
else if (Vector128.IsHardwareAccelerated)
{
var n = count - 3;
for (; i < n; i += 4)
{
var result = Vector128.LoadUnsafe(ref left, i) & Vector128.LoadUnsafe(ref right, i);
result.StoreUnsafe(ref left, i);
}
}
for (; i < count; ++i)
Unsafe.Add(ref left, i) &= Unsafe.Add(ref right, i);
#else
var i = 0;
for (; i < count; ++i)
Unsafe.Add(ref left, i) &= Unsafe.Add(ref right, i);
#endif
}
/// <summary>
/// Or
/// </summary>
/// <param name="destination">Destination</param>
/// <param name="source">Source</param>
/// <param name="count">Count</param>
public static void Or(Span<int> destination, Span<int> source, uint count)
{
switch (count)
{
case 7:
destination[6] |= source[6];
goto case 6;
case 6:
destination[5] |= source[5];
goto case 5;
case 5:
destination[4] |= source[4];
goto case 4;
case 4:
destination[3] |= source[3];
goto case 3;
case 3:
destination[2] |= source[2];
goto case 2;
case 2:
destination[1] |= source[1];
goto case 1;
case 1:
destination[0] |= source[0];
return;
case 0:
return;
}
ref var left = ref MemoryMarshal.GetReference(destination);
ref var right = ref MemoryMarshal.GetReference(source);
#if NET7_0_OR_GREATER
uint i = 0;
if (Vector256.IsHardwareAccelerated)
{
var n = count - 7;
for (; i < n; i += 8)
{
var result = Vector256.LoadUnsafe(ref left, i) | Vector256.LoadUnsafe(ref right, i);
result.StoreUnsafe(ref left, i);
}
}
else if (Vector128.IsHardwareAccelerated)
{
var n = count - 3;
for (; i < n; i += 4)
{
var result = Vector128.LoadUnsafe(ref left, i) | Vector128.LoadUnsafe(ref right, i);
result.StoreUnsafe(ref left, i);
}
}
for (; i < count; ++i)
Unsafe.Add(ref left, i) |= Unsafe.Add(ref right, i);
#else
var i = 0;
for (; i < count; ++i)
Unsafe.Add(ref left, i) |= Unsafe.Add(ref right, i);
#endif
}
/// <summary>
/// Xor
/// </summary>
/// <param name="destination">Destination</param>
/// <param name="source">Source</param>
/// <param name="count">Count</param>
public static void Xor(Span<int> destination, Span<int> source, uint count)
{
switch (count)
{
case 7:
destination[6] ^= source[6];
goto case 6;
case 6:
destination[5] ^= source[5];
goto case 5;
case 5:
destination[4] ^= source[4];
goto case 4;
case 4:
destination[3] ^= source[3];
goto case 3;
case 3:
destination[2] ^= source[2];
goto case 2;
case 2:
destination[1] ^= source[1];
goto case 1;
case 1:
destination[0] ^= source[0];
return;
case 0:
return;
}
ref var left = ref MemoryMarshal.GetReference(destination);
ref var right = ref MemoryMarshal.GetReference(source);
#if NET7_0_OR_GREATER
uint i = 0;
if (Vector256.IsHardwareAccelerated)
{
var n = count - 7;
for (; i < n; i += 8)
{
var result = Vector256.LoadUnsafe(ref left, i) ^ Vector256.LoadUnsafe(ref right, i);
result.StoreUnsafe(ref left, i);
}
}
else if (Vector128.IsHardwareAccelerated)
{
var n = count - 3;
for (; i < n; i += 4)
{
var result = Vector128.LoadUnsafe(ref left, i) ^ Vector128.LoadUnsafe(ref right, i);
result.StoreUnsafe(ref left, i);
}
}
for (; i < count; ++i)
Unsafe.Add(ref left, i) ^= Unsafe.Add(ref right, i);
#else
var i = 0;
for (; i < count; ++i)
Unsafe.Add(ref left, i) ^= Unsafe.Add(ref right, i);
#endif
}
/// <summary>
/// Not
/// </summary>
/// <param name="destination">Destination</param>
/// <param name="count">Count</param>
public static void Not(Span<int> destination, uint count)
{
switch (count)
{
case 7:
destination[6] = ~destination[6];
goto case 6;
case 6:
destination[5] = ~destination[5];
goto case 5;
case 5:
destination[4] = ~destination[4];
goto case 4;
case 4:
destination[3] = ~destination[3];
goto case 3;
case 3:
destination[2] = ~destination[2];
goto case 2;
case 2:
destination[1] = ~destination[1];
goto case 1;
case 1:
destination[0] = ~destination[0];
return;
case 0:
return;
}
ref var value = ref MemoryMarshal.GetReference(destination);
#if NET7_0_OR_GREATER
uint i = 0;
if (Vector256.IsHardwareAccelerated)
{
var n = count - 7;
for (; i < n; i += 8)
{
var result = ~Vector256.LoadUnsafe(ref value, i);
result.StoreUnsafe(ref value, i);
}
}
else if (Vector128.IsHardwareAccelerated)
{
var n = count - 3;
for (; i < n; i += 4)
{
var result = ~Vector128.LoadUnsafe(ref value, i);
result.StoreUnsafe(ref value, i);
}
}
for (; i < count; ++i)
Unsafe.Add(ref value, i) = ~ Unsafe.Add(ref value, i);
#else
var i = 0;
for (; i < count; ++i)
Unsafe.Add(ref value, i) = ~ Unsafe.Add(ref value, i);
#endif
}
}
}

View File

@@ -0,0 +1,125 @@
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Hash helpers
/// </summary>
internal static class HashHelpers
{
/// <summary>
/// Primes
/// </summary>
private static ReadOnlySpan<int> Primes => new int[72]
{
3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,
17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,
187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,
1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369
};
/// <summary>
/// Binary search
/// </summary>
/// <param name="min">Min</param>
/// <returns>Prime</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int BinarySearch(int min)
{
var left = 0;
var right = 71;
ref var value = ref MemoryMarshal.GetReference(Primes);
while (left <= right)
{
var mid = left + (right - left) / 2;
if (Unsafe.Add(ref value, mid) >= min)
right = mid - 1;
else
left = mid + 1;
}
return Unsafe.Add(ref value, left);
}
/// <summary>
/// Is prime
/// </summary>
/// <param name="candidate">Candidate</param>
/// <returns>Is prime</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsPrime(int candidate)
{
if ((candidate & 1) != 0)
{
var limit = (int)Math.Sqrt(candidate);
for (var divisor = 3; divisor <= limit; divisor += 2)
{
if (candidate % divisor == 0)
return false;
}
return true;
}
return candidate == 2;
}
/// <summary>
/// Get prime
/// </summary>
/// <param name="min">Min</param>
/// <returns>Prime</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetPrime(int min)
{
if (min < 0)
throw new ArgumentException("HTCapacityOverflow");
if (min <= 7199369)
return BinarySearch(min);
for (var i = min | 1; i < int.MaxValue; i += 2)
{
if (IsPrime(i) && (i - 1) % 101 != 0)
return i;
}
return min;
}
/// <summary>
/// Expand prime
/// </summary>
/// <param name="oldSize">Old size</param>
/// <returns>Prime</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ExpandPrime(int oldSize)
{
var newSize = 2 * oldSize;
return (uint)newSize > 2147483587 && 2147483587 > oldSize ? 2147483587 : GetPrime(newSize);
}
/// <summary>
/// Get fast mod multiplier
/// </summary>
/// <param name="divisor">Divisor</param>
/// <returns>Fast mod multiplier</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong GetFastModMultiplier(uint divisor) => ulong.MaxValue / divisor + 1;
/// <summary>
/// Fast mod
/// </summary>
/// <param name="value">Value</param>
/// <param name="divisor">Divisor</param>
/// <param name="multiplier">Multiplier</param>
/// <returns>Mod</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint FastMod(uint value, uint divisor, ulong multiplier) => (uint)(((((multiplier * value) >> 32) + 1) * divisor) >> 32);
}
}

View File

@@ -0,0 +1,297 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native array
/// </summary>
/// <typeparam name="T">Type</typeparam>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeArray<T> : IDisposable, IEquatable<NativeArray<T>> where T : unmanaged
{
/// <summary>
/// Array
/// </summary>
private readonly T* _array;
/// <summary>
/// Length
/// </summary>
private readonly int _length;
/// <summary>
/// Structure
/// </summary>
/// <param name="length">Length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArray(int length)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
_array = (T*)NativeMemoryAllocator.Alloc((uint)(length * sizeof(T)));
_length = length;
}
/// <summary>
/// Structure
/// </summary>
/// <param name="length">Length</param>
/// <param name="zeroed">Zeroed</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArray(int length, bool zeroed)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
_array = zeroed ? (T*)NativeMemoryAllocator.AllocZeroed((uint)(length * sizeof(T))) : (T*)NativeMemoryAllocator.Alloc((uint)(length * sizeof(T)));
_length = length;
}
/// <summary>
/// Structure
/// </summary>
/// <param name="array">Array</param>
/// <param name="length">Length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArray(T* array, int length)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
_array = array;
_length = length;
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _array != null;
/// <summary>
/// Is empty
/// </summary>
public bool IsEmpty => _length == 0;
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public ref T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _array[index];
}
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public ref T this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _array[index];
}
/// <summary>
/// Array
/// </summary>
public T* Array => _array;
/// <summary>
/// Length
/// </summary>
public int Length => _length;
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeArray<T> other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeArray<T> nativeArray && nativeArray == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_array;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"NativeArray<{typeof(T).Name}>[{_length}]";
/// <summary>
/// As span
/// </summary>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Span<T>(NativeArray<T> nativeArray) => nativeArray.AsSpan();
/// <summary>
/// As readOnly span
/// </summary>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(NativeArray<T> nativeArray) => nativeArray.AsReadOnlySpan();
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeArray<T> left, NativeArray<T> right) => left._length == right._length && left._array == right._array;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeArray<T> left, NativeArray<T> right) => left._length != right._length || left._array != right._array;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_array == null)
return;
NativeMemoryAllocator.Free(_array);
}
/// <summary>
/// Clear
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() => Unsafe.InitBlockUnaligned(_array, 0, (uint)(_length * sizeof(T)));
/// <summary>
/// As span
/// </summary>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref *_array, _length);
/// <summary>
/// As span
/// </summary>
/// <param name="length">Length</param>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan(int length) => MemoryMarshal.CreateSpan(ref *_array, length);
/// <summary>
/// As span
/// </summary>
/// <param name="start">Start</param>
/// <param name="length">Length</param>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan(int start, int length) => MemoryMarshal.CreateSpan(ref *(_array + start), length);
/// <summary>
/// As readOnly span
/// </summary>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> AsReadOnlySpan() => MemoryMarshal.CreateReadOnlySpan(ref *_array, _length);
/// <summary>
/// As readOnly span
/// </summary>
/// <param name="length">Length</param>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> AsReadOnlySpan(int length) => MemoryMarshal.CreateReadOnlySpan(ref *_array, length);
/// <summary>
/// As readOnly span
/// </summary>
/// <param name="start">Start</param>
/// <param name="length">Length</param>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> AsReadOnlySpan(int start, int length) => MemoryMarshal.CreateReadOnlySpan(ref *(_array + start), length);
/// <summary>
/// Empty
/// </summary>
public static NativeArray<T> Empty => new();
/// <summary>
/// Get enumerator
/// </summary>
/// <returns>Enumerator</returns>
public Enumerator GetEnumerator() => new(this);
/// <summary>
/// Enumerator
/// </summary>
public ref struct Enumerator
{
/// <summary>
/// NativeArray
/// </summary>
private readonly NativeArray<T> _nativeArray;
/// <summary>
/// Index
/// </summary>
private int _index;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeArray">NativeArray</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(NativeArray<T> nativeArray)
{
_nativeArray = nativeArray;
_index = -1;
}
/// <summary>
/// Move next
/// </summary>
/// <returns>Moved</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
var index = _index + 1;
if (index < _nativeArray._length)
{
_index = index;
return true;
}
return false;
}
/// <summary>
/// Current
/// </summary>
public ref T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _nativeArray[_index];
}
}
}
}

View File

@@ -0,0 +1,372 @@
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
using System.Threading;
#endif
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if NET5_0_OR_GREATER
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// NativeMemoryPool
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeArrayPool<T> : IDisposable, IEquatable<NativeArrayPool<T>> where T : unmanaged
{
/// <summary>
/// Buckets
/// </summary>
private readonly NativeArrayPoolBucket* _buckets;
/// <summary>
/// Length
/// </summary>
private readonly int _length;
/// <summary>
/// Size
/// </summary>
private readonly int _size;
/// <summary>
/// Structure
/// </summary>
/// <param name="size">Size</param>
/// <param name="maxLength">Max length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArrayPool(int size, int maxLength)
{
if (size <= 0)
throw new ArgumentOutOfRangeException(nameof(size), size, "MustBePositive");
if (maxLength < 0)
throw new ArgumentOutOfRangeException(nameof(maxLength), maxLength, "MustBeNonNegative");
if (maxLength > 1073741824)
maxLength = 1073741824;
else if (maxLength < 16)
maxLength = 16;
var length = SelectBucketIndex(maxLength) + 1;
var buckets = (NativeArrayPoolBucket*)NativeMemoryAllocator.Alloc((uint)(length * sizeof(NativeArrayPoolBucket)));
for (var i = 0; i < length; ++i)
buckets[i].Initialize(size, 16 << i);
_buckets = buckets;
_length = length;
_size = size;
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _buckets != null;
/// <summary>
/// Size
/// </summary>
public int Size => _size;
/// <summary>
/// Max length
/// </summary>
public int MaxLength => 16 << (_length - 1);
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeArrayPool<T> other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeArrayPool<T> nativeArrayPool && nativeArrayPool == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_buckets;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"NativeArrayPool<{typeof(T).Name}>";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeArrayPool<T> left, NativeArrayPool<T> right) => left._buckets == right._buckets;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeArrayPool<T> left, NativeArrayPool<T> right) => left._buckets != right._buckets;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_buckets == null)
return;
for (var i = 0; i < _length; ++i)
_buckets[i].Dispose();
NativeMemoryAllocator.Free(_buckets);
}
/// <summary>
/// Rent buffer
/// </summary>
/// <param name="minimumLength">Minimum buffer length</param>
/// <returns>Buffer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArray<T> Rent(int minimumLength)
{
if (minimumLength < 0)
throw new ArgumentOutOfRangeException(nameof(minimumLength), minimumLength, "MustBeNonNegative");
var index = SelectBucketIndex(minimumLength);
if (index < _length)
return _buckets[index].Rent();
throw new ArgumentOutOfRangeException(nameof(minimumLength), minimumLength, "BiggerThanCollection");
}
/// <summary>
/// Rent buffer
/// </summary>
/// <param name="minimumLength">Minimum buffer length</param>
/// <param name="array">Buffer</param>
/// <returns>Rented</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryRent(int minimumLength, out NativeArray<T> array)
{
if (minimumLength < 0)
{
array = default;
return false;
}
var index = SelectBucketIndex(minimumLength);
if (index < _length)
{
array = _buckets[index].Rent();
return true;
}
array = default;
return false;
}
/// <summary>
/// Return buffer
/// </summary>
/// <param name="array">Buffer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Return(in NativeArray<T> array)
{
var length = array.Length;
if (length < 16 || (length & (length - 1)) != 0)
throw new ArgumentException("BufferNotFromPool", nameof(array));
var bucket = SelectBucketIndex(length);
if (bucket >= _length)
throw new ArgumentException("BufferNotFromPool", nameof(array));
_buckets[bucket].Return(array.Array);
}
/// <summary>
/// Try return buffer
/// </summary>
/// <param name="array">Buffer</param>
/// <returns>Returned</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryReturn(in NativeArray<T> array)
{
var length = array.Length;
if (length < 16 || (length & (length - 1)) != 0)
return false;
var bucket = SelectBucketIndex(length);
if (bucket >= _length)
return false;
_buckets[bucket].Return(array.Array);
return true;
}
/// <summary>
/// Return buffer
/// </summary>
/// <param name="length">Length</param>
/// <param name="array">Buffer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Return(int length, T* array)
{
if (length < 16 || (length & (length - 1)) != 0)
throw new ArgumentException("BufferNotFromPool", nameof(array));
var bucket = SelectBucketIndex(length);
if (bucket >= _length)
throw new ArgumentException("BufferNotFromPool", nameof(array));
_buckets[bucket].Return(array);
}
/// <summary>
/// Try return buffer
/// </summary>
/// <param name="length">Length</param>
/// <param name="array">Buffer</param>
/// <returns>Returned</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryReturn(int length, T* array)
{
if (length < 16 || (length & (length - 1)) != 0)
return false;
var bucket = SelectBucketIndex(length);
if (bucket >= _length)
return false;
_buckets[bucket].Return(array);
return true;
}
/// <summary>
/// Select bucket index
/// </summary>
/// <param name="bufferSize">Buffer size</param>
/// <returns>Bucket index</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int SelectBucketIndex(int bufferSize) => BitOperationsHelpers.Log2(((uint)bufferSize - 1) | 15) - 3;
/// <summary>
/// Empty
/// </summary>
public static NativeArrayPool<T> Empty => new();
/// <summary>
/// NativeArrayPool bucket
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct NativeArrayPoolBucket : IDisposable
{
/// <summary>
/// Size
/// </summary>
private int _size;
/// <summary>
/// Length
/// </summary>
private int _length;
/// <summary>
/// Buffers
/// </summary>
private T** _array;
/// <summary>
/// Index
/// </summary>
private int _index;
/// <summary>
/// Memory pool
/// </summary>
private NativeMemoryPool _memoryPool;
/// <summary>
/// State lock
/// </summary>
private SpinLock _lock;
/// <summary>
/// Structure
/// </summary>
/// <param name="size">Size</param>
/// <param name="length">Length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Initialize(int size, int length)
{
_size = size;
_length = length;
_array = (T**)NativeMemoryAllocator.AllocZeroed((uint)(size * sizeof(T*)));
_index = 0;
_memoryPool = new NativeMemoryPool(size, length * sizeof(T), 0);
_lock = new SpinLock();
}
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
NativeMemoryAllocator.Free(_array);
_memoryPool.Dispose();
}
/// <summary>
/// Rent buffer
/// </summary>
/// <returns>Buffer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArray<T> Rent()
{
T* ptr = null;
var lockTaken = false;
try
{
_lock.Enter(ref lockTaken);
if (_index < _size)
{
ptr = _array[_index];
_array[_index++] = null;
}
if (ptr == null)
ptr = (T*)_memoryPool.Rent();
}
finally
{
if (lockTaken)
_lock.Exit(false);
}
return new NativeArray<T>(ptr, _length);
}
/// <summary>
/// Return buffer
/// </summary>
/// <param name="ptr">Pointer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Return(T* ptr)
{
var lockTaken = false;
try
{
_lock.Enter(ref lockTaken);
if (_index != 0)
_array[--_index] = ptr;
else
_memoryPool.Return(ptr);
}
finally
{
if (lockTaken)
_lock.Exit(false);
}
}
}
}
}

View File

@@ -0,0 +1,248 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8600
#pragma warning disable CS8603
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native array reference
/// </summary>
/// <typeparam name="T">Type</typeparam>
[StructLayout(LayoutKind.Sequential)]
public struct NativeArrayReference<T> : IDisposable, IEquatable<NativeArrayReference<T>>
{
/// <summary>
/// Handle
/// </summary>
private GCHandle _handle;
/// <summary>
/// Length
/// </summary>
private readonly int _length;
/// <summary>
/// Structure
/// </summary>
/// <param name="length">Length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArrayReference(int length)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
_handle = GCHandle.Alloc(new T[length], GCHandleType.Normal);
_length = length;
}
/// <summary>
/// Structure
/// </summary>
/// <param name="length">Length</param>
/// <param name="type">GCHandle type</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArrayReference(int length, GCHandleType type)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
_handle = GCHandle.Alloc(new T[length], type);
_length = length;
}
/// <summary>
/// Structure
/// </summary>
/// <param name="array">Array</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArrayReference(T[] array)
{
if (array == null)
throw new ArgumentNullException(nameof(array), "MustBeNotNull");
_handle = GCHandle.Alloc(array, GCHandleType.Normal);
_length = array.Length;
}
/// <summary>
/// Structure
/// </summary>
/// <param name="array">Array</param>
/// <param name="type">GCHandle type</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArrayReference(T[] array, GCHandleType type)
{
if (array == null)
throw new ArgumentNullException(nameof(array), "MustBeNotNull");
_handle = GCHandle.Alloc(array, type);
_length = array.Length;
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle.IsAllocated;
/// <summary>
/// Is empty
/// </summary>
public bool IsEmpty => _length == 0;
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public ref T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref Array[index];
}
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public ref T this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref Array[index];
}
/// <summary>
/// Array
/// </summary>
public T[] Array
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (T[])_handle.Target;
}
/// <summary>
/// Length
/// </summary>
public int Length => _length;
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeArrayReference<T> other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeArrayReference<T> nativeArrayReference && nativeArrayReference == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"NativeArrayReference<{typeof(T).Name}>";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeArrayReference<T> left, NativeArrayReference<T> right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeArrayReference<T> left, NativeArrayReference<T> right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (!_handle.IsAllocated)
return;
_handle.Free();
}
/// <summary>
/// Empty
/// </summary>
public static NativeArrayReference<T> Empty => new();
/// <summary>
/// Get enumerator
/// </summary>
/// <returns>Enumerator</returns>
public Enumerator GetEnumerator() => new(Array);
/// <summary>
/// Enumerator
/// </summary>
public ref struct Enumerator
{
/// <summary>
/// Array
/// </summary>
private readonly T[] _array;
/// <summary>
/// Index
/// </summary>
private int _index;
/// <summary>
/// Structure
/// </summary>
/// <param name="array">Array</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(T[] array)
{
_array = array;
_index = -1;
}
/// <summary>
/// Move next
/// </summary>
/// <returns>Moved</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
var index = _index + 1;
if (index < _array.Length)
{
_index = index;
return true;
}
return false;
}
/// <summary>
/// Current
/// </summary>
public ref T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _array[_index];
}
}
}
}

View File

@@ -0,0 +1,386 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native array segment
/// </summary>
/// <typeparam name="T">Type</typeparam>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeArraySegment<T> : IDisposable, IEquatable<NativeArraySegment<T>> where T : unmanaged
{
/// <summary>
/// Array
/// </summary>
private readonly T* _array;
/// <summary>
/// Offset
/// </summary>
private readonly int _offset;
/// <summary>
/// Count
/// </summary>
private readonly int _count;
/// <summary>
/// Structure
/// </summary>
/// <param name="array">Array</param>
/// <param name="count">Count</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArraySegment(T* array, int count)
{
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count), count, "MustBeNonNegative");
_array = array;
_offset = 0;
_count = count;
}
/// <summary>
/// Structure
/// </summary>
/// <param name="array">Array</param>
/// <param name="offset">Offset</param>
/// <param name="count">Count</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArraySegment(T* array, int offset, int count)
{
if (offset < 0)
throw new ArgumentOutOfRangeException(nameof(offset), offset, "MustBeNonNegative");
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count), count, "MustBeNonNegative");
_array = array;
_offset = offset;
_count = count;
}
/// <summary>
/// Structure
/// </summary>
/// <param name="array">Array</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArraySegment(NativeArray<T> array)
{
_array = array.Array;
_offset = 0;
_count = array.Length;
}
/// <summary>
/// Structure
/// </summary>
/// <param name="array">Array</param>
/// <param name="offset">Offset</param>
/// <param name="count">Count</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArraySegment(NativeArray<T> array, int offset, int count)
{
_array = array.Array;
_offset = offset;
_count = count;
}
/// <summary>
/// Structure
/// </summary>
/// <param name="array">Array</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArraySegment(NativeMemoryArray<T> array)
{
_array = array.Array;
_offset = 0;
_count = array.Length;
}
/// <summary>
/// Structure
/// </summary>
/// <param name="array">Array</param>
/// <param name="offset">Offset</param>
/// <param name="count">Count</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArraySegment(NativeMemoryArray<T> array, int offset, int count)
{
_array = array.Array;
_offset = offset;
_count = count;
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _array != null;
/// <summary>
/// Is empty
/// </summary>
public bool IsEmpty => _count == 0;
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public ref T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _array[_offset + index];
}
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public ref T this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _array[_offset + index];
}
/// <summary>
/// Array
/// </summary>
public T* Array => _array;
/// <summary>
/// Offset
/// </summary>
public int Offset => _offset;
/// <summary>
/// Count
/// </summary>
public int Count => _count;
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeArraySegment<T> other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeArraySegment<T> nativeArraySegment && nativeArraySegment == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_array;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"NativeArraySegment<{typeof(T).Name}>[{_offset}, {_count}]";
/// <summary>
/// As span
/// </summary>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Span<T>(NativeArraySegment<T> nativeArraySegment) => nativeArraySegment.AsSpan();
/// <summary>
/// As readOnly span
/// </summary>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(NativeArraySegment<T> nativeArraySegment) => nativeArraySegment.AsReadOnlySpan();
/// <summary>
/// As native array
/// </summary>
/// <param name="nativeArraySegment">Native array segment</param>
/// <returns>NativeArray</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NativeArray<T>(NativeArraySegment<T> nativeArraySegment) => new(nativeArraySegment._array, nativeArraySegment._offset + nativeArraySegment._count);
/// <summary>
/// As native array segment
/// </summary>
/// <param name="nativeArray">Native array</param>
/// <returns>NativeArraySegment</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NativeArraySegment<T>(NativeArray<T> nativeArray) => new(nativeArray);
/// <summary>
/// As native array segment
/// </summary>
/// <param name="nativeArray">Native array</param>
/// <returns>NativeArraySegment</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NativeArraySegment<T>(NativeMemoryArray<T> nativeArray) => new(nativeArray);
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeArraySegment<T> left, NativeArraySegment<T> right) => left._offset == right._offset && left._count == right._count && left._array == right._array;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeArraySegment<T> left, NativeArraySegment<T> right) => left._offset != right._offset || left._count != right._count || left._array != right._array;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_array == null)
return;
NativeMemoryAllocator.Free(_array);
}
/// <summary>
/// As span
/// </summary>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref *(_array + _offset), _count);
/// <summary>
/// As span
/// </summary>
/// <param name="count">Count</param>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan(int count) => MemoryMarshal.CreateSpan(ref *(_array + _offset), count);
/// <summary>
/// As span
/// </summary>
/// <param name="start">Start</param>
/// <param name="count">Count</param>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan(int start, int count) => MemoryMarshal.CreateSpan(ref *(_array + _offset + start), count);
/// <summary>
/// As readOnly span
/// </summary>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> AsReadOnlySpan() => MemoryMarshal.CreateReadOnlySpan(ref *(_array + _offset), _count);
/// <summary>
/// As readOnly span
/// </summary>
/// <param name="count">Count</param>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> AsReadOnlySpan(int count) => MemoryMarshal.CreateReadOnlySpan(ref *(_array + _offset), count);
/// <summary>
/// As readOnly span
/// </summary>
/// <param name="start">Start</param>
/// <param name="count">Count</param>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> AsReadOnlySpan(int start, int count) => MemoryMarshal.CreateReadOnlySpan(ref *(_array + _offset + start), count);
/// <summary>
/// Slice
/// </summary>
/// <param name="start">Start</param>
/// <returns>NativeArraySegment</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArraySegment<T> Slice(int start) => new(_array, _offset + start, _count - start);
/// <summary>
/// Slice
/// </summary>
/// <param name="start">Start</param>
/// <param name="count">Count</param>
/// <returns>NativeArraySegment</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeArraySegment<T> Slice(int start, int count) => new(_array, _offset + start, count);
/// <summary>
/// Empty
/// </summary>
public static NativeArraySegment<T> Empty => new();
/// <summary>
/// Get enumerator
/// </summary>
/// <returns>Enumerator</returns>
public Enumerator GetEnumerator() => new(this);
/// <summary>
/// Enumerator
/// </summary>
public ref struct Enumerator
{
/// <summary>
/// NativeArraySegment
/// </summary>
private readonly NativeArraySegment<T> _nativeArraySegment;
/// <summary>
/// Index
/// </summary>
private int _index;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeArraySegment">NativeArraySegment</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(NativeArraySegment<T> nativeArraySegment)
{
_nativeArraySegment = nativeArraySegment;
_index = -1;
}
/// <summary>
/// Move next
/// </summary>
/// <returns>Moved</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
var index = _index + 1;
if (index < _nativeArraySegment._count)
{
_index = index;
return true;
}
return false;
}
/// <summary>
/// Current
/// </summary>
public ref T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _nativeArraySegment[_index];
}
}
}
}

View File

@@ -0,0 +1,590 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native bit array
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeBitArray : IDisposable, IEquatable<NativeBitArray>
{
/// <summary>
/// Handle
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct NativeBitArrayHandle
{
/// <summary>
/// Array
/// </summary>
public NativeArray<int> Array;
/// <summary>
/// Length
/// </summary>
public int Length;
}
/// <summary>
/// Handle
/// </summary>
private readonly NativeBitArrayHandle* _handle;
/// <summary>
/// Structure
/// </summary>
/// <param name="length">Length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeBitArray(int length)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
_handle = (NativeBitArrayHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeBitArrayHandle));
_handle->Array = new NativeArray<int>(GetInt32ArrayLengthFromBitLength(length));
_handle->Length = length;
}
/// <summary>
/// Structure
/// </summary>
/// <param name="length">Length</param>
/// <param name="defaultValue">Default value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeBitArray(int length, bool defaultValue)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
_handle = (NativeBitArrayHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeBitArrayHandle));
_handle->Array = new NativeArray<int>(GetInt32ArrayLengthFromBitLength(length));
_handle->Length = length;
if (defaultValue)
{
_handle->Array.AsSpan().Fill(-1);
Div32Rem(length, out var extraBits);
if (extraBits > 0)
_handle->Array[^1] = (1 << extraBits) - 1;
}
else
{
_handle->Array.Clear();
}
}
/// <summary>
/// Structure
/// </summary>
/// <param name="array">Array</param>
/// <param name="length">Length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeBitArray(int* array, int length)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
_handle = (NativeBitArrayHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeBitArrayHandle));
_handle->Array = new NativeArray<int>(array, GetInt32ArrayLengthFromBitLength(length));
_handle->Length = length;
}
/// <summary>
/// Structure
/// </summary>
/// <param name="array">Array</param>
/// <param name="length">Length</param>
/// <param name="defaultValue">Default value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeBitArray(int* array, int length, bool defaultValue)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
_handle = (NativeBitArrayHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeBitArrayHandle));
_handle->Array = new NativeArray<int>(array, GetInt32ArrayLengthFromBitLength(length));
_handle->Length = length;
if (defaultValue)
{
_handle->Array.AsSpan().Fill(-1);
Div32Rem(length, out var extraBits);
if (extraBits > 0)
_handle->Array[^1] = (1 << extraBits) - 1;
}
else
{
_handle->Array.Clear();
}
}
/// <summary>
/// Structure
/// </summary>
/// <param name="array">Array</param>
/// <param name="length">Length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeBitArray(NativeArray<int> array, int length)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
var intCount = GetInt32ArrayLengthFromBitLength(length);
if (array.Length < intCount)
throw new ArgumentOutOfRangeException(nameof(array), array.Length, $"Requires size is {intCount}, but buffer length is {array.Length}.");
_handle = (NativeBitArrayHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeBitArrayHandle));
_handle->Array = array;
_handle->Length = length;
}
/// <summary>
/// Structure
/// </summary>
/// <param name="array">Array</param>
/// <param name="length">Length</param>
/// <param name="defaultValue">Default value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeBitArray(NativeArray<int> array, int length, bool defaultValue)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
var intCount = GetInt32ArrayLengthFromBitLength(length);
if (array.Length < intCount)
throw new ArgumentOutOfRangeException(nameof(array), array.Length, $"Requires size is {intCount}, but buffer length is {array.Length}.");
_handle = (NativeBitArrayHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeBitArrayHandle));
_handle->Array = array;
_handle->Length = length;
if (defaultValue)
{
_handle->Array.AsSpan().Fill(-1);
Div32Rem(length, out var extraBits);
if (extraBits > 0)
_handle->Array[^1] = (1 << extraBits) - 1;
}
else
{
_handle->Array.Clear();
}
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle != null;
/// <summary>
/// Array
/// </summary>
public NativeArray<int> Array => _handle->Array;
/// <summary>
/// Length
/// </summary>
public int Length
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _handle->Length;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value), value, "MustBeNonNegative");
var newLength = GetInt32ArrayLengthFromBitLength(value);
if (newLength > _handle->Array.Length || newLength + 256 < _handle->Array.Length)
{
var array = new NativeArray<int>(newLength);
Unsafe.CopyBlockUnaligned(array.Array, _handle->Array.Array, (uint)(_handle->Array.Length * sizeof(int)));
Unsafe.InitBlockUnaligned(array.Array + _handle->Array.Length, 0, (uint)(newLength - _handle->Array.Length));
_handle->Array.Dispose();
_handle->Array = array;
}
if (value > _handle->Length)
{
var last = (_handle->Length - 1) >> 5;
Div32Rem(_handle->Length, out var bits);
if (bits > 0)
_handle->Array[last] &= (1 << bits) - 1;
_handle->Array.AsSpan(last + 1, newLength - last - 1).Clear();
}
_handle->Length = value;
}
}
/// <summary>
/// Get or set value
/// </summary>
/// <param name="index">Index</param>
public bool this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Get(index);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => Set(index, value);
}
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeBitArray other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeBitArray nativeBitArray && nativeBitArray == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => "NativeBitArray";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeBitArray left, NativeBitArray right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeBitArray left, NativeBitArray right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_handle == null)
return;
_handle->Array.Dispose();
NativeMemoryAllocator.Free(_handle);
}
/// <summary>
/// Get
/// </summary>
/// <param name="index">Index</param>
/// <returns>Value</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Get(int index)
{
if ((uint)index >= (uint)_handle->Length)
throw new ArgumentOutOfRangeException(nameof(index), index, "IndexMustBeLess");
return (_handle->Array[index >> 5] & (1 << index)) != 0;
}
/// <summary>
/// </summary>
/// <param name="index">Index</param>
/// <param name="value">Value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Set(int index, bool value)
{
if ((uint)index >= (uint)_handle->Length)
throw new ArgumentOutOfRangeException(nameof(index), index, "IndexMustBeLess");
var bitMask = 1 << index;
ref var segment = ref _handle->Array[index >> 5];
if (value)
segment |= bitMask;
else
segment &= ~bitMask;
}
/// <summary>
/// Set all
/// </summary>
/// <param name="value">Value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetAll(bool value)
{
var arrayLength = GetInt32ArrayLengthFromBitLength(Length);
var span = _handle->Array.AsSpan(0, arrayLength);
if (value)
{
span.Fill(-1);
Div32Rem(_handle->Length, out var extraBits);
if (extraBits > 0)
span[^1] &= (1 << extraBits) - 1;
}
else
{
span.Clear();
}
}
/// <summary>
/// And
/// </summary>
/// <param name="value">Value</param>
/// <returns>NativeBitArray</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeBitArray And(NativeBitArray value)
{
if (!value.IsCreated)
throw new ArgumentNullException(nameof(value));
var count = GetInt32ArrayLengthFromBitLength(Length);
if (Length != value.Length || (uint)count > (uint)_handle->Array.Length || (uint)count > (uint)value._handle->Array.Length)
throw new ArgumentException("ArrayLengthsDiffer");
BitOperationsHelpers.And(_handle->Array, value._handle->Array, (uint)count);
return this;
}
/// <summary>
/// Or
/// </summary>
/// <param name="value">Value</param>
/// <returns>NativeBitArray</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeBitArray Or(NativeBitArray value)
{
if (!value.IsCreated)
throw new ArgumentNullException(nameof(value));
var count = GetInt32ArrayLengthFromBitLength(Length);
if (Length != value.Length || (uint)count > (uint)_handle->Array.Length || (uint)count > (uint)value._handle->Array.Length)
throw new ArgumentException("ArrayLengthsDiffer");
BitOperationsHelpers.Or(_handle->Array, value._handle->Array, (uint)count);
return this;
}
/// <summary>
/// Xor
/// </summary>
/// <param name="value">Value</param>
/// <returns>NativeBitArray</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeBitArray Xor(NativeBitArray value)
{
if (!value.IsCreated)
throw new ArgumentNullException(nameof(value));
var count = GetInt32ArrayLengthFromBitLength(Length);
if (Length != value.Length || (uint)count > (uint)_handle->Array.Length || (uint)count > (uint)value._handle->Array.Length)
throw new ArgumentException("ArrayLengthsDiffer");
BitOperationsHelpers.Xor(_handle->Array, value._handle->Array, (uint)count);
return this;
}
/// <summary>
/// Not
/// </summary>
/// <returns>NativeBitArray</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeBitArray Not()
{
var count = GetInt32ArrayLengthFromBitLength(Length);
BitOperationsHelpers.Not(_handle->Array, (uint)count);
return this;
}
/// <summary>
/// Right shift
/// </summary>
/// <param name="count">Count</param>
/// <returns>NativeBitArray</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeBitArray RightShift(int count)
{
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count), count, "MustBeNonNegative");
if (count == 0)
return this;
var toIndex = 0;
var length = GetInt32ArrayLengthFromBitLength(_handle->Length);
if (count < _handle->Length)
{
var fromIndex = Div32Rem(count, out var shiftCount);
Div32Rem(_handle->Length, out var extraBits);
if (shiftCount == 0)
{
unchecked
{
var mask = uint.MaxValue >> (32 - extraBits);
_handle->Array[length - 1] &= (int)mask;
}
Unsafe.CopyBlockUnaligned(_handle->Array.Array, _handle->Array.Array + fromIndex, (uint)((length - fromIndex) * sizeof(int)));
toIndex = length - fromIndex;
}
else
{
var lastIndex = length - 1;
unchecked
{
while (fromIndex < lastIndex)
{
var right = (uint)_handle->Array[fromIndex] >> shiftCount;
var left = _handle->Array[++fromIndex] << (32 - shiftCount);
_handle->Array[toIndex++] = left | (int)right;
}
var mask = uint.MaxValue >> (32 - extraBits);
mask &= (uint)_handle->Array[fromIndex];
_handle->Array[toIndex++] = (int)(mask >> shiftCount);
}
}
}
_handle->Array.AsSpan(toIndex, length - toIndex).Clear();
return this;
}
/// <summary>
/// Left shift
/// </summary>
/// <param name="count">Count</param>
/// <returns>NativeBitArray</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeBitArray LeftShift(int count)
{
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count), count, "MustBeNonNegative");
if (count == 0)
return this;
int lengthToClear;
if (count < _handle->Length)
{
var lastIndex = (_handle->Length - 1) >> 5;
lengthToClear = Div32Rem(count, out var shiftCount);
if (shiftCount == 0)
{
Unsafe.CopyBlockUnaligned(_handle->Array.Array + lengthToClear, _handle->Array.Array, (uint)((lastIndex + 1 - lengthToClear) * sizeof(int)));
}
else
{
var fromIndex = lastIndex - lengthToClear;
unchecked
{
while (fromIndex > 0)
{
var left = _handle->Array[fromIndex] << shiftCount;
var right = (uint)_handle->Array[--fromIndex] >> (32 - shiftCount);
_handle->Array[lastIndex] = left | (int)right;
lastIndex--;
}
_handle->Array[lastIndex] = _handle->Array[fromIndex] << shiftCount;
}
}
}
else
{
lengthToClear = GetInt32ArrayLengthFromBitLength(_handle->Length);
}
_handle->Array.AsSpan(0, lengthToClear).Clear();
return this;
}
/// <summary>
/// Has all set
/// </summary>
/// <returns>All set</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasAllSet()
{
Div32Rem(_handle->Length, out var extraBits);
var intCount = GetInt32ArrayLengthFromBitLength(_handle->Length);
if (extraBits != 0)
intCount--;
#if NET8_0_OR_GREATER
if (_handle->Array.AsSpan(0, intCount).ContainsAnyExcept(-1))
return false;
#elif NET7_0_OR_GREATER
if (_handle->Array.AsSpan(0, intCount).IndexOfAnyExcept(-1) >= 0)
return false;
#else
for (var i = 0; i < intCount; ++i)
{
if (_handle->Array[i] != -1)
return false;
}
#endif
if (extraBits == 0)
return true;
var mask = (1 << extraBits) - 1;
return (_handle->Array[intCount] & mask) == mask;
}
/// <summary>
/// Has any set
/// </summary>
/// <returns>Any set</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasAnySet()
{
Div32Rem(_handle->Length, out var extraBits);
var intCount = GetInt32ArrayLengthFromBitLength(_handle->Length);
if (extraBits != 0)
intCount--;
#if NET8_0_OR_GREATER
if (_handle->Array.AsSpan(0, intCount).ContainsAnyExcept(0))
return true;
#elif NET7_0_OR_GREATER
if (_handle->Array.AsSpan(0, intCount).IndexOfAnyExcept(0) >= 0)
return true;
#else
for (var i = 0; i < intCount; ++i)
{
if (_handle->Array[i] != 0)
return true;
}
#endif
if (extraBits == 0)
return false;
return (_handle->Array[intCount] & ((1 << extraBits) - 1)) != 0;
}
/// <summary>
/// Get int32 array length from bit length
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetInt32ArrayLengthFromBitLength(int n)
{
#if NET7_0_OR_GREATER
return (n - 1 + (1 << 5)) >>> 5;
#else
return (int)((uint)(n - 1 + (1 << 5)) >> 5);
#endif
}
/// <summary>
/// Divide by 32 and get remainder
/// </summary>
/// <param name="number">Number</param>
/// <param name="remainder">Remainder</param>
/// <returns>Quotient</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int Div32Rem(int number, out int remainder)
{
var quotient = (uint)number / 32;
remainder = number & (32 - 1);
return (int)quotient;
}
/// <summary>
/// Empty
/// </summary>
public static NativeBitArray Empty => new();
}
}

View File

@@ -0,0 +1,227 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native buddy memory pool
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeBuddyMemoryPool : IDisposable, IEquatable<NativeBuddyMemoryPool>
{
/// <summary>
/// Min block size
/// </summary>
private readonly int _minBlockSize;
/// <summary>
/// Max block size
/// </summary>
private readonly int _maxBlockSize;
/// <summary>
/// Bit map
/// </summary>
private readonly int* _bitmap;
/// <summary>
/// Memory
/// </summary>
private readonly byte* _memory;
/// <summary>
/// Structure
/// </summary>
/// <param name="minBlockSize">Min block size</param>
/// <param name="maxBlockSize">Max block size</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeBuddyMemoryPool(int minBlockSize, int maxBlockSize)
{
if (minBlockSize > maxBlockSize)
throw new ArgumentException($"{minBlockSize} cannot be greater than {maxBlockSize}.");
if (minBlockSize <= 0)
throw new ArgumentOutOfRangeException(nameof(minBlockSize), minBlockSize, "MustBePositive");
if ((minBlockSize & (minBlockSize - 1)) != 0)
throw new ArgumentOutOfRangeException(nameof(minBlockSize), minBlockSize, "MustBePowOf2");
if ((maxBlockSize & (maxBlockSize - 1)) != 0)
throw new ArgumentOutOfRangeException(nameof(maxBlockSize), maxBlockSize, "MustBePowOf2");
_minBlockSize = minBlockSize;
_maxBlockSize = maxBlockSize;
var bitmapSize = ((1 << (BitOperationsHelpers.Log2(maxBlockSize / minBlockSize) + 1)) + 31) / 32 * sizeof(int);
var array = (byte*)NativeMemoryAllocator.Alloc((uint)(bitmapSize + maxBlockSize));
_bitmap = (int*)array;
_memory = array + bitmapSize;
Unsafe.InitBlockUnaligned(array, 0, (uint)bitmapSize);
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _bitmap != null;
/// <summary>
/// Min block size
/// </summary>
public int MinBlockSize => _minBlockSize;
/// <summary>
/// Max block size
/// </summary>
public int MaxBlockSize => _maxBlockSize;
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeBuddyMemoryPool other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeBuddyMemoryPool nativeBuddyMemoryPool && nativeBuddyMemoryPool == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_bitmap;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => "NativeBuddyMemoryPool";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeBuddyMemoryPool left, NativeBuddyMemoryPool right) => left._bitmap == right._bitmap;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeBuddyMemoryPool left, NativeBuddyMemoryPool right) => left._bitmap != right._bitmap;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_bitmap == null)
return;
NativeMemoryAllocator.Free(_bitmap);
}
/// <summary>
/// Get layer
/// </summary>
/// <param name="size">Size</param>
/// <returns>Layer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int GetLayer(int size) => size <= _minBlockSize ? 0 : BitOperationsHelpers.Log2((uint)((size - 1) / _minBlockSize)) + 1;
/// <summary>
/// Find free block
/// </summary>
/// <param name="layer">Layer</param>
/// <returns>Free block</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int FindFreeBlock(int layer)
{
var blocksInLayer = 1 << layer;
var offset = blocksInLayer - 1;
for (var i = 0; i < blocksInLayer; ++i)
{
var index = offset + i;
if ((_bitmap[index / 32] & (1 << (index % 32))) == 0)
return index;
}
return -1;
}
/// <summary>
/// Merge blocks
/// </summary>
/// <param name="layer">Layer</param>
/// <param name="index">Index</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void MergeBlocks(int layer, int index)
{
while (layer != 0)
{
var buddyIndex = index % 2 == 0 ? index + 1 : index - 1;
var bitMask = buddyIndex % 32;
ref var segment = ref _bitmap[buddyIndex / 32];
if ((segment & (1 << bitMask)) != 0)
break;
var parentIndex = index / 2 + ((1 << (layer - 1)) - 1);
(*(_bitmap + index / 32)) &= ~(1 << (index % 32));
segment &= ~(1 << bitMask);
(*(_bitmap + parentIndex / 32)) |= 1 << (parentIndex % 32);
--layer;
index = parentIndex;
}
}
/// <summary>
/// Rent buffer
/// </summary>
/// <returns>Buffer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void* Rent(int size)
{
if (size > _maxBlockSize)
throw new ArgumentOutOfRangeException(nameof(size), $"{size} cannot be greater than {_maxBlockSize}.");
if (size <= 0)
throw new ArgumentOutOfRangeException(nameof(size), size, "MustBePositive");
var layer = GetLayer(size);
var blockIndex = FindFreeBlock(layer);
if (blockIndex == -1)
return null;
_bitmap[blockIndex / 32] |= 1 << (blockIndex % 32);
return _memory + (_minBlockSize << layer) * (blockIndex - ((1 << layer) - 1));
}
/// <summary>
/// Return buffer
/// </summary>
/// <param name="ptr">Pointer</param>
/// <param name="size">Size</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Return(void* ptr, int size)
{
if (size > _maxBlockSize)
throw new ArgumentOutOfRangeException(nameof(size), $"{size} cannot be greater than {_maxBlockSize}.");
if (size <= 0)
throw new ArgumentOutOfRangeException(nameof(size), size, "MustBePositive");
var layer = GetLayer(size);
var blockIndex = (int)((byte*)ptr - _memory) / (_minBlockSize << layer) + ((1 << layer) - 1);
_bitmap[blockIndex / 32] &= ~(1 << (blockIndex % 32));
MergeBlocks(layer, blockIndex);
}
/// <summary>
/// Empty
/// </summary>
public static NativeBuddyMemoryPool Empty => new();
}
}

View File

@@ -0,0 +1,878 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
using System.Threading;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native concurrentHashSet
/// (Slower than ConcurrentHashSet)
/// </summary>
/// <typeparam name="T">Type</typeparam>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeConcurrentHashSet<T> : IDisposable, IEquatable<NativeConcurrentHashSet<T>> where T : unmanaged, IEquatable<T>
{
/// <summary>
/// Handle
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct NativeConcurrentHashSetHandle
{
/// <summary>
/// Tables
/// </summary>
public volatile Tables* Tables;
/// <summary>
/// Budget
/// </summary>
public int Budget;
/// <summary>
/// Grow lock array
/// </summary>
public bool GrowLockArray;
/// <summary>
/// Node pool
/// </summary>
public NativeMemoryPool NodePool;
/// <summary>
/// Node lock
/// </summary>
public NativeConcurrentSpinLock NodeLock;
}
/// <summary>
/// Handle
/// </summary>
private readonly NativeConcurrentHashSetHandle* _handle;
/// <summary>
/// Structure
/// </summary>
/// <param name="size">Size</param>
/// <param name="maxFreeSlabs">Max free slabs</param>
/// <param name="concurrencyLevel">Concurrency level</param>
/// <param name="capacity">Capacity</param>
/// <param name="growLockArray">Grow lock array</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeConcurrentHashSet(int size, int maxFreeSlabs, int concurrencyLevel, int capacity, bool growLockArray)
{
var nodePool = new NativeMemoryPool(size, sizeof(Node), maxFreeSlabs);
if (concurrencyLevel <= 0)
concurrencyLevel = Environment.ProcessorCount;
if (capacity < concurrencyLevel)
capacity = concurrencyLevel;
capacity = HashHelpers.GetPrime(capacity);
var locks = new NativeArrayReference<object>(concurrencyLevel);
for (var i = 0; i < locks.Length; ++i)
locks[i] = new object();
var countPerLock = new NativeArray<int>(locks.Length, true);
var buckets = new NativeArray<VolatileNode>(capacity, true);
_handle = (NativeConcurrentHashSetHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeConcurrentHashSetHandle));
_handle->Tables = (Tables*)NativeMemoryAllocator.Alloc((uint)sizeof(Tables));
_handle->Tables->Initialize(buckets, locks, countPerLock);
_handle->GrowLockArray = growLockArray;
_handle->Budget = buckets.Length / locks.Length;
_handle->NodePool = nodePool;
_handle->NodeLock = new NativeConcurrentSpinLock(-1);
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle != null;
/// <summary>
/// Is created
/// </summary>
public bool IsEmpty
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (!AreAllBucketsEmpty())
return false;
var locksAcquired = 0;
try
{
AcquireAllLocks(ref locksAcquired);
return AreAllBucketsEmpty();
}
finally
{
ReleaseLocks(locksAcquired);
}
}
}
/// <summary>
/// Count
/// </summary>
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
var locksAcquired = 0;
try
{
AcquireAllLocks(ref locksAcquired);
return GetCountNoLocks();
}
finally
{
ReleaseLocks(locksAcquired);
}
}
}
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeConcurrentHashSet<T> other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeConcurrentHashSet<T> nativeConcurrentHashSet && nativeConcurrentHashSet == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"NativeConcurrentHashSet<{typeof(T).Name}>";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeConcurrentHashSet<T> left, NativeConcurrentHashSet<T> right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeConcurrentHashSet<T> left, NativeConcurrentHashSet<T> right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_handle == null)
return;
_handle->Tables->Dispose();
_handle->NodePool.Dispose();
_handle->NodeLock.Dispose();
NativeMemoryAllocator.Free(_handle);
}
/// <summary>
/// Clear
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
var locksAcquired = 0;
try
{
AcquireAllLocks(ref locksAcquired);
if (AreAllBucketsEmpty())
return;
foreach (var bucket in _handle->Tables->Buckets)
{
var node = (Node*)bucket.Node;
while (node != null)
{
var temp = node;
node = node->Next;
_handle->NodePool.Return(temp);
}
}
var length = HashHelpers.GetPrime(31);
if (_handle->Tables->Buckets.Length != length)
{
_handle->Tables->Buckets.Dispose();
_handle->Tables->Buckets = new NativeArray<VolatileNode>(length, true);
}
else
{
_handle->Tables->Buckets.Clear();
}
_handle->Tables->CountPerLock.Clear();
var budget = _handle->Tables->Buckets.Length / _handle->Tables->Locks.Length;
_handle->Budget = budget >= 1 ? budget : 1;
}
finally
{
ReleaseLocks(locksAcquired);
}
}
/// <summary>
/// Add
/// </summary>
/// <param name="key">Key</param>
/// <returns>Added</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Add(in T key) => TryAddInternal(_handle->Tables, key);
/// <summary>
/// Remove
/// </summary>
/// <param name="key">Key</param>
/// <returns>Removed</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(in T key)
{
var tables = _handle->Tables;
var hashCode = key.GetHashCode();
while (true)
{
var locks = tables->Locks;
ref var bucket = ref GetBucketAndLock(tables, hashCode, out var lockNo);
if (tables->CountPerLock[lockNo] != 0)
{
Monitor.Enter(locks[lockNo]);
try
{
if (tables != _handle->Tables)
{
tables = _handle->Tables;
continue;
}
Node* prev = null;
for (var curr = (Node*)bucket; curr != null; curr = curr->Next)
{
if (hashCode == curr->HashCode && curr->Key.Equals(key))
{
if (prev == null)
Volatile.Write(ref bucket, (nint)curr->Next);
else
prev->Next = curr->Next;
_handle->NodeLock.Enter();
try
{
_handle->NodePool.Return(curr);
}
finally
{
_handle->NodeLock.Exit();
}
tables->CountPerLock[lockNo]--;
return true;
}
prev = curr;
}
}
finally
{
Monitor.Exit(locks[lockNo]);
}
}
return false;
}
}
/// <summary>
/// Contains key
/// </summary>
/// <param name="key">Key</param>
/// <returns>Contains key</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(in T key)
{
var tables = _handle->Tables;
var hashCode = key.GetHashCode();
for (var node = (Node*)GetBucket(tables, hashCode); node != null; node = node->Next)
{
if (hashCode == node->HashCode && node->Key.Equals(key))
return true;
}
return false;
}
/// <summary>
/// Try to get the actual value
/// </summary>
/// <param name="equalValue">Equal value</param>
/// <param name="actualValue">Actual value</param>
/// <returns>Got</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(in T equalValue, out T actualValue)
{
var tables = _handle->Tables;
var hashCode = equalValue.GetHashCode();
for (var node = (Node*)GetBucket(tables, hashCode); node != null; node = node->Next)
{
if (hashCode == node->HashCode && node->Key.Equals(equalValue))
{
actualValue = node->Key;
return true;
}
}
actualValue = default;
return false;
}
/// <summary>
/// Check all buckets are empty
/// </summary>
/// <returns>All buckets are empty</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool AreAllBucketsEmpty()
{
#if NET8_0_OR_GREATER
return !_handle->Tables->CountPerLock.AsSpan().ContainsAnyExcept(0);
#elif NET7_0_OR_GREATER
return !(_handle->Tables->CountPerLock.AsSpan().IndexOfAnyExcept(0) >= 0);
#else
for (var i = 0; i < _handle->Tables->CountPerLock.Length; ++i)
{
if (_handle->Tables->CountPerLock[i] != 0)
return false;
}
return true;
#endif
}
/// <summary>
/// Grow table
/// </summary>
/// <param name="tables">Tables</param>
/// <param name="resizeDesired">Resize desired</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void GrowTable(Tables* tables, bool resizeDesired)
{
var locksAcquired = 0;
try
{
AcquireFirstLock(ref locksAcquired);
if (tables != _handle->Tables)
return;
var newLength = tables->Buckets.Length;
if (resizeDesired)
{
if (GetCountNoLocks() < tables->Buckets.Length / 4)
{
_handle->Budget = 2 * _handle->Budget;
if (_handle->Budget < 0)
_handle->Budget = int.MaxValue;
return;
}
if ((newLength = tables->Buckets.Length * 2) < 0 || (newLength = HashHelpers.GetPrime(newLength)) > 2147483591)
{
newLength = 2147483591;
_handle->Budget = int.MaxValue;
}
}
var newLocks = tables->Locks;
if (_handle->GrowLockArray && tables->Locks.Length < 1024)
{
newLocks = new NativeArrayReference<object>(tables->Locks.Length * 2);
Array.Copy(tables->Locks.Array, newLocks.Array, tables->Locks.Length);
for (var i = tables->Locks.Length; i < newLocks.Length; ++i)
newLocks[i] = new NativeMonitorLock(new object());
}
var newBuckets = new NativeArray<VolatileNode>(newLength, true);
var newCountPerLock = new NativeArray<int>(newLocks.Length, true);
var newTables = (Tables*)NativeMemoryAllocator.Alloc((uint)sizeof(Tables));
newTables->Initialize(newBuckets, newLocks, newCountPerLock);
AcquirePostFirstLock(tables, ref locksAcquired);
foreach (var bucket in tables->Buckets)
{
var current = (Node*)bucket.Node;
while (current != null)
{
var hashCode = current->HashCode;
var next = current->Next;
ref var newBucket = ref GetBucketAndLock(newTables, hashCode, out var newLockNo);
var newNode = current;
newNode->Initialize(current->Key, hashCode, (Node*)newBucket);
newBucket = (nint)newNode;
checked
{
newCountPerLock[newLockNo]++;
}
current = next;
}
}
var budget = newBuckets.Length / newLocks.Length;
_handle->Budget = budget >= 1 ? budget : 1;
_handle->Tables->Buckets.Dispose();
if (_handle->Tables->Locks != newLocks)
_handle->Tables->Locks.Dispose();
_handle->Tables->CountPerLock.Dispose();
NativeMemoryAllocator.Free(_handle->Tables);
_handle->Tables = newTables;
}
finally
{
ReleaseLocks(locksAcquired);
}
}
/// <summary>
/// Acquire all locks
/// </summary>
/// <param name="locksAcquired">Locks acquired</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AcquireAllLocks(ref int locksAcquired)
{
AcquireFirstLock(ref locksAcquired);
AcquirePostFirstLock(_handle->Tables, ref locksAcquired);
}
/// <summary>
/// Acquire first lock
/// </summary>
/// <param name="locksAcquired">Locks acquired</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AcquireFirstLock(ref int locksAcquired)
{
var locks = _handle->Tables->Locks;
Monitor.Enter(locks[0]);
locksAcquired = 1;
}
/// <summary>
/// Acquire post first locks
/// </summary>
/// <param name="tables">Tables</param>
/// <param name="locksAcquired">Locks acquired</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AcquirePostFirstLock(Tables* tables, ref int locksAcquired)
{
var locks = tables->Locks;
for (var i = 1; i < locks.Length; ++i)
{
Monitor.Enter(locks[i]);
locksAcquired++;
}
}
/// <summary>
/// Release locks
/// </summary>
/// <param name="locksAcquired">Locks acquired</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReleaseLocks(int locksAcquired)
{
var locks = _handle->Tables->Locks;
for (var i = 0; i < locksAcquired; ++i)
Monitor.Exit(locks[i]);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool TryAddInternal(Tables* tables, in T key)
{
var hashCode = key.GetHashCode();
while (true)
{
var locks = tables->Locks;
ref var bucket = ref GetBucketAndLock(tables, hashCode, out var lockNo);
var resizeDesired = false;
var lockTaken = false;
try
{
Monitor.Enter(locks[lockNo], ref lockTaken);
if (tables != _handle->Tables)
{
tables = _handle->Tables;
continue;
}
for (var node = (Node*)bucket; node != null; node = node->Next)
{
if (hashCode == node->HashCode && node->Key.Equals(key))
return false;
}
Node* resultNode;
_handle->NodeLock.Enter();
try
{
resultNode = (Node*)_handle->NodePool.Rent();
}
finally
{
_handle->NodeLock.Exit();
}
resultNode->Initialize(key, hashCode, (Node*)bucket);
Volatile.Write(ref bucket, (nint)resultNode);
checked
{
tables->CountPerLock[lockNo]++;
}
if (tables->CountPerLock[lockNo] > _handle->Budget)
resizeDesired = true;
}
finally
{
if (lockTaken)
Monitor.Exit(locks[lockNo]);
}
if (resizeDesired)
GrowTable(tables, resizeDesired);
return true;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int GetCountNoLocks()
{
var count = 0;
foreach (var value in _handle->Tables->CountPerLock)
{
checked
{
count += value;
}
}
return count;
}
/// <summary>
/// Get bucket
/// </summary>
/// <param name="tables">Tables</param>
/// <param name="hashCode">HashCode</param>
/// <returns>Bucket</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static nint GetBucket(Tables* tables, int hashCode)
{
var buckets = tables->Buckets;
return IntPtr.Size == 8 ? buckets[HashHelpers.FastMod((uint)hashCode, (uint)buckets.Length, tables->FastModBucketsMultiplier)].Node : buckets[(uint)hashCode % (uint)buckets.Length].Node;
}
/// <summary>
/// Get bucket and lock
/// </summary>
/// <param name="tables">Tables</param>
/// <param name="hashCode">HashCode</param>
/// <param name="lockNo">Lock no</param>
/// <returns>Bucket</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ref nint GetBucketAndLock(Tables* tables, int hashCode, out uint lockNo)
{
var buckets = tables->Buckets;
var bucketNo = IntPtr.Size == 8 ? HashHelpers.FastMod((uint)hashCode, (uint)buckets.Length, tables->FastModBucketsMultiplier) : (uint)hashCode % (uint)buckets.Length;
lockNo = bucketNo % (uint)tables->Locks.Length;
return ref buckets[bucketNo].Node;
}
/// <summary>
/// Volatile node
/// </summary>
private struct VolatileNode
{
/// <summary>
/// Node
/// </summary>
public volatile nint Node;
}
/// <summary>
/// Node
/// </summary>
private struct Node
{
/// <summary>
/// Key
/// </summary>
public T Key;
/// <summary>
/// Next
/// </summary>
public volatile Node* Next;
/// <summary>
/// HashCode
/// </summary>
public int HashCode;
/// <summary>
/// Initialize
/// </summary>
/// <param name="key">Key</param>
/// <param name="hashCode">HashCode</param>
/// <param name="next">Next</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Initialize(in T key, int hashCode, Node* next)
{
Key = key;
Next = next;
HashCode = hashCode;
}
}
/// <summary>
/// Tables
/// </summary>
private struct Tables
{
/// <summary>
/// Buckets
/// </summary>
public NativeArray<VolatileNode> Buckets;
/// <summary>
/// Fast mod buckets multiplier
/// </summary>
public ulong FastModBucketsMultiplier;
/// <summary>
/// Locks
/// </summary>
public NativeArrayReference<object> Locks;
/// <summary>
/// Count per lock
/// </summary>
public NativeArray<int> CountPerLock;
/// <summary>
/// Initialize
/// </summary>
/// <param name="buckets">Buckets</param>
/// <param name="locks">Locks</param>
/// <param name="countPerLock">Count per lock</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Initialize(in NativeArray<VolatileNode> buckets, in NativeArrayReference<object> locks, in NativeArray<int> countPerLock)
{
Buckets = buckets;
Locks = locks;
CountPerLock = countPerLock;
FastModBucketsMultiplier = IntPtr.Size == 8 ? HashHelpers.GetFastModMultiplier((uint)buckets.Length) : 0;
}
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
Buckets.Dispose();
Locks.Dispose();
CountPerLock.Dispose();
}
}
/// <summary>
/// Empty
/// </summary>
public static NativeConcurrentHashSet<T> Empty => new();
/// <summary>
/// Get enumerator
/// </summary>
/// <returns>Enumerator</returns>
public Enumerator GetEnumerator() => new(this);
/// <summary>
/// Enumerator
/// </summary>
public struct Enumerator
{
/// <summary>
/// NativeConcurrentHashSet
/// </summary>
private readonly NativeConcurrentHashSet<T> _nativeConcurrentHashSet;
/// <summary>
/// Buckets
/// </summary>
private NativeArray<VolatileNode> _buckets;
/// <summary>
/// Node
/// </summary>
private Node* _node;
/// <summary>
/// Index
/// </summary>
private int _index;
/// <summary>
/// State
/// </summary>
private int _state;
/// <summary>
/// State uninitialized
/// </summary>
private const int STATE_UNINITIALIZED = 0;
/// <summary>
/// State outer loop
/// </summary>
private const int STATE_OUTER_LOOP = 1;
/// <summary>
/// State inner loop
/// </summary>
private const int STATE_INNER_LOOP = 2;
/// <summary>
/// State done
/// </summary>
private const int STATE_DONE = 3;
/// <summary>
/// Current
/// </summary>
private T _current;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeConcurrentHashSet">NativeConcurrentHashSet</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(NativeConcurrentHashSet<T> nativeConcurrentHashSet)
{
_nativeConcurrentHashSet = nativeConcurrentHashSet;
_index = -1;
_buckets = default;
_node = null;
_state = 0;
_current = default;
}
/// <summary>
/// Move next
/// </summary>
/// <returns>Moved</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
switch (_state)
{
case STATE_UNINITIALIZED:
_buckets = _nativeConcurrentHashSet._handle->Tables->Buckets;
_index = -1;
goto case STATE_OUTER_LOOP;
case STATE_OUTER_LOOP:
var buckets = _buckets;
var i = ++_index;
if ((uint)i < (uint)buckets.Length)
{
_node = (Node*)buckets[i].Node;
_state = STATE_INNER_LOOP;
goto case STATE_INNER_LOOP;
}
goto default;
case STATE_INNER_LOOP:
if (_node != null)
{
var node = _node;
_current = node->Key;
_node = node->Next;
return true;
}
goto case STATE_OUTER_LOOP;
default:
_state = STATE_DONE;
return false;
}
}
/// <summary>
/// Current
/// </summary>
public T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _current;
}
}
}
/// <summary>
/// Native concurrentHashSet type props
/// </summary>
/// <typeparam name="T">Type</typeparam>
internal static class NativeConcurrentHashSetTypeProps<T> where T : unmanaged, IEquatable<T>
{
/// <summary>
/// Is write atomic
/// </summary>
public static readonly bool IsWriteAtomic = IsWriteAtomicPrivate();
/// <summary>
/// Is write atomic
/// </summary>
/// <returns>Is write atomic</returns>
private static bool IsWriteAtomicPrivate()
{
if (typeof(T) == typeof(IntPtr) || typeof(T) == typeof(UIntPtr))
return true;
switch (Type.GetTypeCode(typeof(T)))
{
case TypeCode.Boolean:
case TypeCode.Byte:
case TypeCode.Char:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.SByte:
case TypeCode.Single:
case TypeCode.UInt16:
case TypeCode.UInt32:
return true;
case TypeCode.Double:
case TypeCode.Int64:
case TypeCode.UInt64:
return IntPtr.Size == 8;
default:
return false;
}
}
}
}

View File

@@ -0,0 +1,180 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
using System.Threading;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native concurrent spinLock
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeConcurrentSpinLock : IDisposable, IEquatable<NativeConcurrentSpinLock>
{
/// <summary>
/// Handle
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct NativeConcurrentSpinLockHandle
{
/// <summary>
/// Sequence number
/// </summary>
public int SequenceNumber;
/// <summary>
/// Next sequence number
/// </summary>
public int NextSequenceNumber;
/// <summary>
/// Sleep threshold
/// </summary>
public int SleepThreshold;
}
/// <summary>
/// Handle
/// </summary>
private readonly NativeConcurrentSpinLockHandle* _handle;
/// <summary>
/// Structure
/// </summary>
/// <param name="sleepThreshold">Sleep threshold</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeConcurrentSpinLock(int sleepThreshold)
{
if (sleepThreshold < -1)
sleepThreshold = -1;
else if (sleepThreshold >= 0 && sleepThreshold < 10)
sleepThreshold = 10;
_handle = (NativeConcurrentSpinLockHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeConcurrentSpinLockHandle));
_handle->SequenceNumber = 0;
_handle->NextSequenceNumber = 1;
_handle->SleepThreshold = sleepThreshold;
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle != null;
/// <summary>
/// Sleep threshold
/// </summary>
public int SleepThreshold => _handle->SleepThreshold;
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeConcurrentSpinLock other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeConcurrentSpinLock nativeConcurrentSpinLock && nativeConcurrentSpinLock == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => "NativeConcurrentSpinLock";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeConcurrentSpinLock left, NativeConcurrentSpinLock right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeConcurrentSpinLock left, NativeConcurrentSpinLock right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_handle == null)
return;
NativeMemoryAllocator.Free(_handle);
}
/// <summary>
/// Enter
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Enter()
{
var sequenceNumber = Interlocked.Add(ref _handle->SequenceNumber, 1);
if (sequenceNumber != _handle->NextSequenceNumber)
{
var count = 0;
var sleepThreshold = _handle->SleepThreshold;
do
{
if ((count >= 10 && ((count >= sleepThreshold && sleepThreshold >= 0) || (count - 10) % 2 == 0)) || Environment.ProcessorCount == 1)
{
if (count >= sleepThreshold && sleepThreshold >= 0)
{
Thread.Sleep(1);
}
else
{
var yieldsSoFar = count >= 10 ? (count - 10) / 2 : count;
if (yieldsSoFar % 5 == 4)
Thread.Sleep(0);
else
Thread.Yield();
}
}
else
{
var iterations = Environment.ProcessorCount / 2;
if (count <= 30 && 1 << count < iterations)
iterations = 1 << count;
Thread.SpinWait(iterations);
}
count = count == int.MaxValue ? 10 : count + 1;
} while (sequenceNumber != _handle->NextSequenceNumber);
}
}
/// <summary>
/// Exit
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Exit() => Interlocked.Add(ref _handle->NextSequenceNumber, 1);
/// <summary>
/// Empty
/// </summary>
public static NativeConcurrentSpinLock Empty => new();
}
}

View File

@@ -0,0 +1,338 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if !NET6_0_OR_GREATER
using System.Security.Cryptography;
#endif
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
using System.Threading;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native concurrentStack
/// (Slower than ConcurrentStack, disable Enumerator, try peek, push/pop range either)
/// </summary>
/// <typeparam name="T">Type</typeparam>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeConcurrentStack<T> : IDisposable, IEquatable<NativeConcurrentStack<T>> where T : unmanaged
{
/// <summary>
/// Handle
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct NativeConcurrentStackHandle
{
/// <summary>
/// Head
/// </summary>
public volatile nint Head;
/// <summary>
/// Node pool
/// </summary>
public NativeMemoryPool NodePool;
/// <summary>
/// Node pool lock
/// </summary>
public NativeConcurrentSpinLock NodePoolLock;
}
/// <summary>
/// Handle
/// </summary>
private readonly NativeConcurrentStackHandle* _handle;
/// <summary>
/// Structure
/// </summary>
/// <param name="size">Size</param>
/// <param name="maxFreeSlabs">Max free slabs</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeConcurrentStack(int size, int maxFreeSlabs)
{
var nodePool = new NativeMemoryPool(size, sizeof(Node), maxFreeSlabs);
_handle = (NativeConcurrentStackHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeConcurrentStackHandle));
_handle->Head = IntPtr.Zero;
_handle->NodePool = nodePool;
_handle->NodePoolLock = new NativeConcurrentSpinLock(-1);
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle != null;
/// <summary>
/// IsEmpty
/// </summary>
public bool IsEmpty => _handle->Head == IntPtr.Zero;
/// <summary>
/// Count
/// </summary>
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
var count = 0;
for (var node = (Node*)_handle->Head; node != null; node = node->Next)
count++;
return count;
}
}
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeConcurrentStack<T> other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeConcurrentStack<T> nativeConcurrentStack && nativeConcurrentStack == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"NativeConcurrentStack<{typeof(T).Name}>";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeConcurrentStack<T> left, NativeConcurrentStack<T> right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeConcurrentStack<T> left, NativeConcurrentStack<T> right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_handle == null)
return;
_handle->NodePool.Dispose();
_handle->NodePoolLock.Dispose();
NativeMemoryAllocator.Free(_handle);
}
/// <summary>
/// Clear
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
_handle->NodePoolLock.Enter();
try
{
var node = (Node*)_handle->Head;
while (node != null)
{
var temp = node;
node = node->Next;
_handle->NodePool.Return(temp);
}
}
finally
{
_handle->NodePoolLock.Exit();
}
}
/// <summary>
/// Push
/// </summary>
/// <param name="item">Item</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Push(in T item)
{
Node* newNode;
_handle->NodePoolLock.Enter();
try
{
newNode = (Node*)_handle->NodePool.Rent();
}
finally
{
_handle->NodePoolLock.Exit();
}
newNode->Value = item;
newNode->Next = (Node*)_handle->Head;
if (Interlocked.CompareExchange(ref _handle->Head, (nint)newNode, (nint)newNode->Next) == (nint)newNode->Next)
return;
var count = 0;
do
{
if ((count >= 10 && (count - 10) % 2 == 0) || Environment.ProcessorCount == 1)
{
var yieldsSoFar = count >= 10 ? (count - 10) / 2 : count;
if (yieldsSoFar % 5 == 4)
Thread.Sleep(0);
else
Thread.Yield();
}
else
{
var iterations = Environment.ProcessorCount / 2;
if (count <= 30 && 1 << count < iterations)
iterations = 1 << count;
Thread.SpinWait(iterations);
}
count = count == int.MaxValue ? 10 : count + 1;
newNode->Next = (Node*)_handle->Head;
} while (Interlocked.CompareExchange(ref _handle->Head, (nint)newNode, (nint)newNode->Next) != (nint)newNode->Next);
}
/// <summary>
/// Try pop
/// </summary>
/// <param name="result">Item</param>
/// <returns>Popped</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
var head = (Node*)_handle->Head;
if (head == null)
{
result = default;
return false;
}
if (Interlocked.CompareExchange(ref _handle->Head, (nint)head->Next, (nint)head) == (nint)head)
{
result = head->Value;
_handle->NodePoolLock.Enter();
try
{
_handle->NodePool.Return(head);
}
finally
{
_handle->NodePoolLock.Exit();
}
return true;
}
var count = 0;
var backoff = 1;
#if !NET6_0_OR_GREATER
Span<byte> random = stackalloc byte[1];
#endif
while (true)
{
head = (Node*)_handle->Head;
if (head == null)
{
result = default;
return false;
}
if (Interlocked.CompareExchange(ref _handle->Head, (nint)head->Next, (nint)head) == (nint)head)
{
result = head->Value;
_handle->NodePoolLock.Enter();
try
{
_handle->NodePool.Return(head);
}
finally
{
_handle->NodePoolLock.Exit();
}
return true;
}
for (var i = 0; i < backoff; ++i)
{
if ((count >= 10 && (count - 10) % 2 == 0) || Environment.ProcessorCount == 1)
{
var yieldsSoFar = count >= 10 ? (count - 10) / 2 : count;
if (yieldsSoFar % 5 == 4)
Thread.Sleep(0);
else
Thread.Yield();
}
else
{
var iterations = Environment.ProcessorCount / 2;
if (count <= 30 && 1 << count < iterations)
iterations = 1 << count;
Thread.SpinWait(iterations);
}
count = count == int.MaxValue ? 10 : count + 1;
}
if (count >= 10 || Environment.ProcessorCount == 1)
{
#if NET6_0_OR_GREATER
backoff = Random.Shared.Next(1, 8);
#else
RandomNumberGenerator.Fill(random);
backoff = random[0] % 7 + 1;
#endif
}
else
{
backoff *= 2;
}
}
}
/// <summary>
/// Empty
/// </summary>
public static NativeConcurrentStack<T> Empty => new();
/// <summary>
/// Node
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct Node
{
/// <summary>
/// Value
/// </summary>
public T Value;
/// <summary>
/// Next
/// </summary>
public Node* Next;
}
}
}

View File

@@ -0,0 +1,971 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
using System.Collections.Generic;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native dictionary
/// </summary>
/// <typeparam name="TKey">Type</typeparam>
/// <typeparam name="TValue">Type</typeparam>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeDictionary<TKey, TValue> : IDisposable, IEquatable<NativeDictionary<TKey, TValue>> where TKey : unmanaged, IEquatable<TKey> where TValue : unmanaged
{
/// <summary>
/// Handle
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct NativeDictionaryHandle
{
/// <summary>
/// Buckets
/// </summary>
public int* Buckets;
/// <summary>
/// Entries
/// </summary>
public Entry* Entries;
/// <summary>
/// BucketsLength
/// </summary>
public int BucketsLength;
/// <summary>
/// EntriesLength
/// </summary>
public int EntriesLength;
/// <summary>
/// FastModMultiplier
/// </summary>
public ulong FastModMultiplier;
/// <summary>
/// Count
/// </summary>
public int Count;
/// <summary>
/// FreeList
/// </summary>
public int FreeList;
/// <summary>
/// FreeCount
/// </summary>
public int FreeCount;
/// <summary>
/// Version
/// </summary>
public int Version;
/// <summary>
/// Keys
/// </summary>
public KeyCollection Keys;
/// <summary>
/// Values
/// </summary>
public ValueCollection Values;
}
/// <summary>
/// Handle
/// </summary>
private readonly NativeDictionaryHandle* _handle;
/// <summary>
/// Keys
/// </summary>
public KeyCollection Keys => _handle->Keys;
/// <summary>
/// Values
/// </summary>
public ValueCollection Values => _handle->Values;
/// <summary>
/// Structure
/// </summary>
/// <param name="capacity">Capacity</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeDictionary(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "MustBeNonNegative");
if (capacity < 4)
capacity = 4;
_handle = (NativeDictionaryHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeDictionaryHandle));
_handle->Count = 0;
_handle->FreeCount = 0;
_handle->Version = 0;
Initialize(capacity);
_handle->Keys = new KeyCollection(this);
_handle->Values = new ValueCollection(this);
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle != null;
/// <summary>
/// Is empty
/// </summary>
public bool IsEmpty => _handle->Count - _handle->FreeCount == 0;
/// <summary>
/// Get or set value
/// </summary>
/// <param name="key">Key</param>
public TValue this[in TKey key]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
ref var value = ref FindValue(key);
if (Unsafe.AsPointer(ref Unsafe.AsRef(in value)) != null)
return value;
throw new KeyNotFoundException(key.ToString());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => TryInsertOverwriteExisting(key, value);
}
/// <summary>
/// Count
/// </summary>
public int Count => _handle->Count - _handle->FreeCount;
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeDictionary<TKey, TValue> other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeDictionary<TKey, TValue> nativeDictionary && nativeDictionary == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"NativeDictionary<{typeof(TKey).Name}, {typeof(TValue).Name}>";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeDictionary<TKey, TValue> left, NativeDictionary<TKey, TValue> right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeDictionary<TKey, TValue> left, NativeDictionary<TKey, TValue> right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_handle == null)
return;
NativeMemoryAllocator.Free(_handle->Buckets);
NativeMemoryAllocator.Free(_handle->Entries);
NativeMemoryAllocator.Free(_handle);
}
/// <summary>
/// Clear
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
var count = _handle->Count;
if (count > 0)
{
Unsafe.InitBlockUnaligned(_handle->Buckets, 0, (uint)(count * sizeof(int)));
_handle->Count = 0;
_handle->FreeList = -1;
_handle->FreeCount = 0;
Unsafe.InitBlockUnaligned(_handle->Entries, 0, (uint)(count * sizeof(Entry)));
}
}
/// <summary>
/// Add
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(in TKey key, in TValue value) => TryInsertThrowOnExisting(key, value);
/// <summary>
/// Try add
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
/// <returns>Added</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryAdd(in TKey key, in TValue value) => TryInsertNone(key, value);
/// <summary>
/// Remove
/// </summary>
/// <param name="key">Key</param>
/// <returns>Removed</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(in TKey key)
{
uint collisionCount = 0;
var hashCode = (uint)key.GetHashCode();
ref var bucket = ref GetBucket(hashCode);
var last = -1;
var i = bucket - 1;
while (i >= 0)
{
ref var entry = ref _handle->Entries[i];
if (entry.HashCode == hashCode && entry.Key.Equals(key))
{
if (last < 0)
bucket = entry.Next + 1;
else
_handle->Entries[last].Next = entry.Next;
entry.Next = -3 - _handle->FreeList;
_handle->FreeList = i;
_handle->FreeCount++;
return true;
}
last = i;
i = entry.Next;
collisionCount++;
if (collisionCount > (uint)_handle->EntriesLength)
throw new InvalidOperationException("ConcurrentOperationsNotSupported");
}
return false;
}
/// <summary>
/// Remove
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
/// <returns>Removed</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(in TKey key, out TValue value)
{
uint collisionCount = 0;
var hashCode = (uint)key.GetHashCode();
ref var bucket = ref GetBucket(hashCode);
var last = -1;
var i = bucket - 1;
while (i >= 0)
{
ref var entry = ref _handle->Entries[i];
if (entry.HashCode == hashCode && entry.Key.Equals(key))
{
if (last < 0)
bucket = entry.Next + 1;
else
_handle->Entries[last].Next = entry.Next;
value = entry.Value;
entry.Next = -3 - _handle->FreeList;
_handle->FreeList = i;
_handle->FreeCount++;
return true;
}
last = i;
i = entry.Next;
collisionCount++;
if (collisionCount > (uint)_handle->EntriesLength)
throw new InvalidOperationException("ConcurrentOperationsNotSupported");
}
value = default;
return false;
}
/// <summary>
/// Contains key
/// </summary>
/// <param name="key">Key</param>
/// <returns>Contains key</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ContainsKey(in TKey key) => Unsafe.AsPointer(ref Unsafe.AsRef(in FindValue(key))) != null;
/// <summary>
/// Try to get the value
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
/// <returns>Got</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(in TKey key, out TValue value)
{
ref var valRef = ref FindValue(key);
if (Unsafe.AsPointer(ref Unsafe.AsRef(in valRef)) != null)
{
value = valRef;
return true;
}
value = default;
return false;
}
/// <summary>
/// Ensure capacity
/// </summary>
/// <param name="capacity">Capacity</param>
/// <returns>New capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int EnsureCapacity(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "MustBeNonNegative");
var currentCapacity = _handle->EntriesLength;
if (currentCapacity >= capacity)
return currentCapacity;
_handle->Version++;
var newSize = HashHelpers.GetPrime(capacity);
Resize(newSize);
return newSize;
}
/// <summary>
/// Trim excess
/// </summary>
/// <returns>New capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int TrimExcess() => TrimExcess(Count);
/// <summary>
/// Trim excess
/// </summary>
/// <param name="capacity">Capacity</param>
/// <returns>New capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int TrimExcess(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "MustBeNonNegative");
var newSize = HashHelpers.GetPrime(capacity);
var oldEntries = _handle->Entries;
var currentCapacity = _handle->EntriesLength;
if (newSize >= currentCapacity)
return currentCapacity;
var oldCount = _handle->Count;
_handle->Version++;
NativeMemoryAllocator.Free(_handle->Buckets);
Initialize(newSize);
var newEntries = _handle->Entries;
var newCount = 0;
for (var i = 0; i < oldCount; ++i)
{
var hashCode = oldEntries[i].HashCode;
if (oldEntries[i].Next >= -1)
{
ref var entry = ref newEntries[newCount];
entry = oldEntries[i];
ref var bucket = ref GetBucket(hashCode);
entry.Next = bucket - 1;
bucket = newCount + 1;
newCount++;
}
}
NativeMemoryAllocator.Free(oldEntries);
_handle->Count = newCount;
_handle->FreeCount = 0;
return newSize;
}
/// <summary>
/// Find value
/// </summary>
/// <param name="key">Key</param>
/// <returns>Value</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ref TValue FindValue(in TKey key)
{
var hashCode = (uint)key.GetHashCode();
var i = GetBucket(hashCode);
uint collisionCount = 0;
i--;
do
{
if ((uint)i >= (uint)_handle->EntriesLength)
return ref Unsafe.AsRef<TValue>(null);
ref var entry = ref _handle->Entries[i];
if (entry.HashCode == hashCode && entry.Key.Equals(key))
return ref entry.Value;
i = entry.Next;
collisionCount++;
} while (collisionCount <= (uint)_handle->EntriesLength);
throw new InvalidOperationException("ConcurrentOperationsNotSupported");
}
/// <summary>
/// Initialize
/// </summary>
/// <param name="capacity">Capacity</param>
/// <returns>New capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Initialize(int capacity)
{
var size = HashHelpers.GetPrime(capacity);
_handle->FreeList = -1;
_handle->Buckets = (int*)NativeMemoryAllocator.AllocZeroed((uint)(size * sizeof(int)));
_handle->Entries = (Entry*)NativeMemoryAllocator.AllocZeroed((uint)(size * sizeof(Entry)));
_handle->BucketsLength = size;
_handle->EntriesLength = size;
_handle->FastModMultiplier = IntPtr.Size == 8 ? HashHelpers.GetFastModMultiplier((uint)size) : 0;
}
/// <summary>
/// Resize
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Resize() => Resize(HashHelpers.ExpandPrime(_handle->Count));
/// <summary>
/// Resize
/// </summary>
/// <param name="newSize"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Resize(int newSize)
{
var entries = (Entry*)NativeMemoryAllocator.AllocZeroed((uint)(newSize * sizeof(Entry)));
var count = _handle->Count;
Unsafe.CopyBlockUnaligned(entries, _handle->Entries, (uint)(count * sizeof(Entry)));
var buckets = (int*)NativeMemoryAllocator.AllocZeroed((uint)(newSize * sizeof(int)));
NativeMemoryAllocator.Free(_handle->Buckets);
_handle->Buckets = buckets;
_handle->BucketsLength = newSize;
_handle->FastModMultiplier = IntPtr.Size == 8 ? HashHelpers.GetFastModMultiplier((uint)newSize) : 0;
for (var i = 0; i < count; ++i)
{
if (entries[i].Next >= -1)
{
ref var bucket = ref GetBucket(entries[i].HashCode);
entries[i].Next = bucket - 1;
bucket = i + 1;
}
}
NativeMemoryAllocator.Free(_handle->Entries);
_handle->Entries = entries;
_handle->EntriesLength = newSize;
}
/// <summary>
/// Insert
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void TryInsertOverwriteExisting(in TKey key, in TValue value)
{
var hashCode = (uint)key.GetHashCode();
uint collisionCount = 0;
ref var bucket = ref GetBucket(hashCode);
var i = bucket - 1;
while (true)
{
if ((uint)i >= (uint)_handle->EntriesLength)
break;
if (_handle->Entries[i].HashCode == hashCode && _handle->Entries[i].Key.Equals(key))
{
_handle->Entries[i].Value = value;
return;
}
i = _handle->Entries[i].Next;
collisionCount++;
if (collisionCount > (uint)_handle->EntriesLength)
throw new InvalidOperationException("ConcurrentOperationsNotSupported");
}
int index;
if (_handle->FreeCount > 0)
{
index = _handle->FreeList;
_handle->FreeList = -3 - _handle->Entries[_handle->FreeList].Next;
_handle->FreeCount--;
}
else
{
var count = _handle->Count;
if (count == _handle->EntriesLength)
{
Resize();
bucket = ref GetBucket(hashCode);
}
index = count;
_handle->Count = count + 1;
}
ref var entry = ref _handle->Entries[index];
entry.HashCode = hashCode;
entry.Next = bucket - 1;
entry.Key = key;
entry.Value = value;
bucket = index + 1;
_handle->Version++;
}
/// <summary>
/// Insert
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool TryInsertThrowOnExisting(in TKey key, in TValue value)
{
var hashCode = (uint)key.GetHashCode();
uint collisionCount = 0;
ref var bucket = ref GetBucket(hashCode);
var i = bucket - 1;
while (true)
{
if ((uint)i >= (uint)_handle->EntriesLength)
break;
if (_handle->Entries[i].HashCode == hashCode && _handle->Entries[i].Key.Equals(key))
throw new ArgumentException($"Argument_AddingDuplicateWithKey, {key}");
i = _handle->Entries[i].Next;
collisionCount++;
if (collisionCount > (uint)_handle->EntriesLength)
throw new InvalidOperationException("ConcurrentOperationsNotSupported");
}
int index;
if (_handle->FreeCount > 0)
{
index = _handle->FreeList;
_handle->FreeList = -3 - _handle->Entries[_handle->FreeList].Next;
_handle->FreeCount--;
}
else
{
var count = _handle->Count;
if (count == _handle->EntriesLength)
{
Resize();
bucket = ref GetBucket(hashCode);
}
index = count;
_handle->Count = count + 1;
}
ref var entry = ref _handle->Entries[index];
entry.HashCode = hashCode;
entry.Next = bucket - 1;
entry.Key = key;
entry.Value = value;
bucket = index + 1;
_handle->Version++;
return true;
}
/// <summary>
/// Insert
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool TryInsertNone(in TKey key, in TValue value)
{
var hashCode = (uint)key.GetHashCode();
uint collisionCount = 0;
ref var bucket = ref GetBucket(hashCode);
var i = bucket - 1;
while (true)
{
if ((uint)i >= (uint)_handle->EntriesLength)
break;
if (_handle->Entries[i].HashCode == hashCode && _handle->Entries[i].Key.Equals(key))
return false;
i = _handle->Entries[i].Next;
collisionCount++;
if (collisionCount > (uint)_handle->EntriesLength)
throw new InvalidOperationException("ConcurrentOperationsNotSupported");
}
int index;
if (_handle->FreeCount > 0)
{
index = _handle->FreeList;
_handle->FreeList = -3 - _handle->Entries[_handle->FreeList].Next;
_handle->FreeCount--;
}
else
{
var count = _handle->Count;
if (count == _handle->EntriesLength)
{
Resize();
bucket = ref GetBucket(hashCode);
}
index = count;
_handle->Count = count + 1;
}
ref var entry = ref _handle->Entries[index];
entry.HashCode = hashCode;
entry.Next = bucket - 1;
entry.Key = key;
entry.Value = value;
bucket = index + 1;
_handle->Version++;
return true;
}
/// <summary>
/// Get bucket ref
/// </summary>
/// <param name="hashCode">HashCode</param>
/// <returns>Bucket ref</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ref int GetBucket(uint hashCode) => ref IntPtr.Size == 8 ? ref _handle->Buckets[HashHelpers.FastMod(hashCode, (uint)_handle->BucketsLength, _handle->FastModMultiplier)] : ref _handle->Buckets[hashCode % _handle->BucketsLength];
/// <summary>
/// Entry
/// </summary>
private struct Entry
{
/// <summary>
/// HashCode
/// </summary>
public uint HashCode;
/// <summary>
/// Next
/// </summary>
public int Next;
/// <summary>
/// Key
/// </summary>
public TKey Key;
/// <summary>
/// Value
/// </summary>
public TValue Value;
}
/// <summary>
/// Empty
/// </summary>
public static NativeDictionary<TKey, TValue> Empty => new();
/// <summary>
/// Get enumerator
/// </summary>
/// <returns>Enumerator</returns>
public Enumerator GetEnumerator() => new(this);
/// <summary>
/// Enumerator
/// </summary>
public struct Enumerator
{
/// <summary>
/// NativeDictionary
/// </summary>
private readonly NativeDictionary<TKey, TValue> _nativeDictionary;
/// <summary>
/// Version
/// </summary>
private readonly int _version;
/// <summary>
/// Index
/// </summary>
private int _index;
/// <summary>
/// Current
/// </summary>
private KeyValuePair<TKey, TValue> _current;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeDictionary">NativeDictionary</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(in NativeDictionary<TKey, TValue> nativeDictionary)
{
_nativeDictionary = nativeDictionary;
_version = nativeDictionary._handle->Version;
_index = 0;
_current = default;
}
/// <summary>
/// Move next
/// </summary>
/// <returns>Moved</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
if (_version != _nativeDictionary._handle->Version)
throw new InvalidOperationException("EnumFailedVersion");
while ((uint)_index < (uint)_nativeDictionary._handle->Count)
{
ref var entry = ref _nativeDictionary._handle->Entries[_index++];
if (entry.Next >= -1)
{
_current = new KeyValuePair<TKey, TValue>(entry.Key, entry.Value);
return true;
}
}
_index = _nativeDictionary._handle->Count + 1;
_current = default;
return false;
}
/// <summary>
/// Current
/// </summary>
public KeyValuePair<TKey, TValue> Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _current;
}
}
/// <summary>
/// Key collection
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public readonly struct KeyCollection
{
/// <summary>
/// NativeDictionary
/// </summary>
private readonly NativeDictionary<TKey, TValue> _nativeDictionary;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeDictionary">NativeDictionary</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal KeyCollection(in NativeDictionary<TKey, TValue> nativeDictionary) => _nativeDictionary = nativeDictionary;
/// <summary>
/// Get enumerator
/// </summary>
/// <returns>Enumerator</returns>
public Enumerator GetEnumerator() => new(_nativeDictionary);
/// <summary>
/// Enumerator
/// </summary>
public struct Enumerator
{
/// <summary>
/// NativeDictionary
/// </summary>
private readonly NativeDictionary<TKey, TValue> _nativeDictionary;
/// <summary>
/// Index
/// </summary>
private int _index;
/// <summary>
/// Version
/// </summary>
private readonly int _version;
/// <summary>
/// Current
/// </summary>
private TKey _currentKey;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeDictionary">NativeDictionary</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(in NativeDictionary<TKey, TValue> nativeDictionary)
{
_nativeDictionary = nativeDictionary;
_version = nativeDictionary._handle->Version;
_index = 0;
_currentKey = default;
}
/// <summary>
/// Move next
/// </summary>
/// <returns>Moved</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
if (_version != _nativeDictionary._handle->Version)
throw new InvalidOperationException("EnumFailedVersion");
while ((uint)_index < (uint)_nativeDictionary._handle->Count)
{
ref var entry = ref _nativeDictionary._handle->Entries[_index++];
if (entry.Next >= -1)
{
_currentKey = entry.Key;
return true;
}
}
_index = _nativeDictionary._handle->Count + 1;
_currentKey = default;
return false;
}
/// <summary>
/// Current
/// </summary>
public TKey Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _currentKey;
}
}
}
/// <summary>
/// Value collection
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public readonly struct ValueCollection
{
/// <summary>
/// NativeDictionary
/// </summary>
private readonly NativeDictionary<TKey, TValue> _nativeDictionary;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeDictionary">NativeDictionary</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ValueCollection(in NativeDictionary<TKey, TValue> nativeDictionary) => _nativeDictionary = nativeDictionary;
/// <summary>
/// Get enumerator
/// </summary>
/// <returns>Enumerator</returns>
public Enumerator GetEnumerator() => new(_nativeDictionary);
/// <summary>
/// Enumerator
/// </summary>
public struct Enumerator
{
/// <summary>
/// NativeDictionary
/// </summary>
private readonly NativeDictionary<TKey, TValue> _nativeDictionary;
/// <summary>
/// Index
/// </summary>
private int _index;
/// <summary>
/// Version
/// </summary>
private readonly int _version;
/// <summary>
/// Current
/// </summary>
private TValue _currentValue;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeDictionary">NativeDictionary</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(in NativeDictionary<TKey, TValue> nativeDictionary)
{
_nativeDictionary = nativeDictionary;
_version = nativeDictionary._handle->Version;
_index = 0;
_currentValue = default;
}
/// <summary>
/// Move next
/// </summary>
/// <returns>Moved</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
if (_version != _nativeDictionary._handle->Version)
throw new InvalidOperationException("EnumFailedVersion");
while ((uint)_index < (uint)_nativeDictionary._handle->Count)
{
ref var entry = ref _nativeDictionary._handle->Entries[_index++];
if (entry.Next >= -1)
{
_currentValue = entry.Value;
return true;
}
}
_index = _nativeDictionary._handle->Count + 1;
_currentValue = default;
return false;
}
/// <summary>
/// Current
/// </summary>
public TValue Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _currentValue;
}
}
}
}
}

View File

@@ -0,0 +1,550 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native hashSet
/// </summary>
/// <typeparam name="T">Type</typeparam>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeHashSet<T> : IDisposable, IEquatable<NativeHashSet<T>> where T : unmanaged, IEquatable<T>
{
/// <summary>
/// Handle
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct NativeHashSetHandle
{
/// <summary>
/// Buckets
/// </summary>
public int* Buckets;
/// <summary>
/// Entries
/// </summary>
public Entry* Entries;
/// <summary>
/// BucketsLength
/// </summary>
public int BucketsLength;
/// <summary>
/// EntriesLength
/// </summary>
public int EntriesLength;
/// <summary>
/// FastModMultiplier
/// </summary>
public ulong FastModMultiplier;
/// <summary>
/// Count
/// </summary>
public int Count;
/// <summary>
/// FreeList
/// </summary>
public int FreeList;
/// <summary>
/// FreeCount
/// </summary>
public int FreeCount;
/// <summary>
/// Version
/// </summary>
public int Version;
}
/// <summary>
/// Handle
/// </summary>
private readonly NativeHashSetHandle* _handle;
/// <summary>
/// Structure
/// </summary>
/// <param name="capacity">Capacity</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeHashSet(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "MustBeNonNegative");
if (capacity < 4)
capacity = 4;
_handle = (NativeHashSetHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeHashSetHandle));
_handle->Count = 0;
_handle->FreeCount = 0;
_handle->Version = 0;
Initialize(capacity);
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle != null;
/// <summary>
/// Is empty
/// </summary>
public bool IsEmpty => _handle->Count - _handle->FreeCount == 0;
/// <summary>
/// Count
/// </summary>
public int Count => _handle->Count - _handle->FreeCount;
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeHashSet<T> other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeHashSet<T> nativeHashSet && nativeHashSet == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"NativeHashSet<{typeof(T).Name}>";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeHashSet<T> left, NativeHashSet<T> right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeHashSet<T> left, NativeHashSet<T> right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_handle == null)
return;
NativeMemoryAllocator.Free(_handle->Buckets);
NativeMemoryAllocator.Free(_handle->Entries);
NativeMemoryAllocator.Free(_handle);
}
/// <summary>
/// Clear
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
var count = _handle->Count;
if (count > 0)
{
Unsafe.InitBlockUnaligned(_handle->Buckets, 0, (uint)(_handle->BucketsLength * sizeof(int)));
_handle->Count = 0;
_handle->FreeList = -1;
_handle->FreeCount = 0;
Unsafe.InitBlockUnaligned(_handle->Entries, 0, (uint)(count * sizeof(Entry)));
}
}
/// <summary>
/// Add
/// </summary>
/// <param name="item">Item</param>
/// <returns>Added</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Add(in T item)
{
uint collisionCount = 0;
var hashCode = item.GetHashCode();
ref var bucket = ref GetBucketRef(hashCode);
var i = bucket - 1;
while (i >= 0)
{
ref var entry = ref _handle->Entries[i];
if (entry.HashCode == hashCode && entry.Value.Equals(item))
return false;
i = entry.Next;
collisionCount++;
if (collisionCount > (uint)_handle->EntriesLength)
throw new InvalidOperationException("ConcurrentOperationsNotSupported");
}
int index;
if (_handle->FreeCount > 0)
{
index = _handle->FreeList;
_handle->FreeCount--;
_handle->FreeList = -3 - _handle->Entries[_handle->FreeList].Next;
}
else
{
var count = _handle->Count;
if (count == _handle->EntriesLength)
{
Resize();
bucket = ref GetBucketRef(hashCode);
}
index = count;
_handle->Count = count + 1;
}
ref var newEntry = ref _handle->Entries[index];
newEntry.HashCode = hashCode;
newEntry.Next = bucket - 1;
newEntry.Value = item;
bucket = index + 1;
_handle->Version++;
return true;
}
/// <summary>
/// Remove
/// </summary>
/// <param name="item">Item</param>
/// <returns>Removed</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(in T item)
{
uint collisionCount = 0;
var last = -1;
var hashCode = item.GetHashCode();
ref var bucket = ref GetBucketRef(hashCode);
var i = bucket - 1;
while (i >= 0)
{
ref var entry = ref _handle->Entries[i];
if (entry.HashCode == hashCode && entry.Value.Equals(item))
{
if (last < 0)
bucket = entry.Next + 1;
else
_handle->Entries[last].Next = entry.Next;
entry.Next = -3 - _handle->FreeList;
_handle->FreeList = i;
_handle->FreeCount++;
return true;
}
last = i;
i = entry.Next;
collisionCount++;
if (collisionCount > (uint)_handle->EntriesLength)
throw new InvalidOperationException("ConcurrentOperationsNotSupported");
}
return false;
}
/// <summary>
/// Contains
/// </summary>
/// <param name="item">Item</param>
/// <returns>Contains</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(in T item) => FindItemIndex(item) >= 0;
/// <summary>
/// Try to get the actual value
/// </summary>
/// <param name="equalValue">Equal value</param>
/// <param name="actualValue">Actual value</param>
/// <returns>Got</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(in T equalValue, out T actualValue)
{
var index = FindItemIndex(equalValue);
if (index >= 0)
{
actualValue = _handle->Entries[index].Value;
return true;
}
actualValue = default;
return false;
}
/// <summary>
/// Ensure capacity
/// </summary>
/// <param name="capacity">Capacity</param>
/// <returns>New capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int EnsureCapacity(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "MustBeNonNegative");
var currentCapacity = _handle->EntriesLength;
if (currentCapacity >= capacity)
return currentCapacity;
var newSize = HashHelpers.GetPrime(capacity);
Resize(newSize);
return newSize;
}
/// <summary>
/// Trim excess
/// </summary>
/// <returns>New capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int TrimExcess()
{
var capacity = _handle->Count - _handle->FreeCount;
var newSize = HashHelpers.GetPrime(capacity);
var oldEntries = _handle->Entries;
var currentCapacity = _handle->EntriesLength;
if (newSize >= currentCapacity)
return currentCapacity;
var oldCount = _handle->Count;
_handle->Version++;
NativeMemoryAllocator.Free(_handle->Buckets);
Initialize(newSize);
var newEntries = _handle->Entries;
var count = 0;
for (var i = 0; i < oldCount; ++i)
{
var hashCode = oldEntries[i].HashCode;
if (oldEntries[i].Next >= -1)
{
ref var entry = ref newEntries[count];
entry = oldEntries[i];
ref var bucket = ref GetBucketRef(hashCode);
entry.Next = bucket - 1;
bucket = count + 1;
count++;
}
}
NativeMemoryAllocator.Free(oldEntries);
_handle->Count = capacity;
_handle->FreeCount = 0;
return newSize;
}
/// <summary>
/// Initialize
/// </summary>
/// <param name="capacity">Capacity</param>
/// <returns>New capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int Initialize(int capacity)
{
var size = HashHelpers.GetPrime(capacity);
_handle->FreeList = -1;
_handle->Buckets = (int*)NativeMemoryAllocator.AllocZeroed((uint)(size * sizeof(int)));
_handle->Entries = (Entry*)NativeMemoryAllocator.AllocZeroed((uint)(size * sizeof(Entry)));
_handle->BucketsLength = size;
_handle->EntriesLength = size;
_handle->FastModMultiplier = IntPtr.Size == 8 ? HashHelpers.GetFastModMultiplier((uint)size) : 0;
return size;
}
/// <summary>
/// Resize
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Resize() => Resize(HashHelpers.ExpandPrime(_handle->Count));
/// <summary>
/// Resize
/// </summary>
/// <param name="newSize"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Resize(int newSize)
{
var entries = (Entry*)NativeMemoryAllocator.AllocZeroed((uint)(newSize * sizeof(Entry)));
var count = _handle->Count;
Unsafe.CopyBlockUnaligned(entries, _handle->Entries, (uint)(_handle->EntriesLength * sizeof(Entry)));
var buckets = (int*)NativeMemoryAllocator.AllocZeroed((uint)(newSize * sizeof(int)));
NativeMemoryAllocator.Free(_handle->Buckets);
_handle->Buckets = buckets;
_handle->BucketsLength = newSize;
_handle->FastModMultiplier = IntPtr.Size == 8 ? HashHelpers.GetFastModMultiplier((uint)newSize) : 0;
for (var i = 0; i < count; ++i)
{
ref var entry = ref entries[i];
if (entry.Next >= -1)
{
ref var bucket = ref GetBucketRef(entry.HashCode);
entry.Next = bucket - 1;
bucket = i + 1;
}
}
NativeMemoryAllocator.Free(_handle->Entries);
_handle->Entries = entries;
_handle->EntriesLength = newSize;
}
/// <summary>
/// Find item index
/// </summary>
/// <param name="item">Item</param>
/// <returns>Index</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int FindItemIndex(in T item)
{
uint collisionCount = 0;
var hashCode = item.GetHashCode();
var i = GetBucketRef(hashCode) - 1;
while (i >= 0)
{
ref var entry = ref _handle->Entries[i];
if (entry.HashCode == hashCode && entry.Value.Equals(item))
return i;
i = entry.Next;
collisionCount++;
if (collisionCount > (uint)_handle->EntriesLength)
throw new InvalidOperationException("ConcurrentOperationsNotSupported");
}
return -1;
}
/// <summary>
/// Get bucket ref
/// </summary>
/// <param name="hashCode">HashCode</param>
/// <returns>Bucket ref</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ref int GetBucketRef(int hashCode) => ref IntPtr.Size == 8 ? ref _handle->Buckets[HashHelpers.FastMod((uint)hashCode, (uint)_handle->BucketsLength, _handle->FastModMultiplier)] : ref _handle->Buckets[(uint)hashCode % (uint)_handle->BucketsLength];
/// <summary>
/// Entry
/// </summary>
private struct Entry
{
/// <summary>
/// HashCode
/// </summary>
public int HashCode;
/// <summary>
/// Next
/// </summary>
public int Next;
/// <summary>
/// Value
/// </summary>
public T Value;
}
/// <summary>
/// Empty
/// </summary>
public static NativeHashSet<T> Empty => new();
/// <summary>
/// Get enumerator
/// </summary>
/// <returns>Enumerator</returns>
public Enumerator GetEnumerator() => new(this);
/// <summary>
/// Enumerator
/// </summary>
public struct Enumerator
{
/// <summary>
/// NativeHashSet
/// </summary>
private readonly NativeHashSet<T> _nativeHashSet;
/// <summary>
/// Version
/// </summary>
private readonly int _version;
/// <summary>
/// Index
/// </summary>
private int _index;
/// <summary>
/// Current
/// </summary>
private T _current;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeHashSet">NativeHashSet</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(in NativeHashSet<T> nativeHashSet)
{
_nativeHashSet = nativeHashSet;
_version = nativeHashSet._handle->Version;
_index = 0;
_current = default;
}
/// <summary>
/// Move next
/// </summary>
/// <returns>Moved</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
if (_version != _nativeHashSet._handle->Version)
throw new InvalidOperationException("EnumFailedVersion");
while ((uint)_index < (uint)_nativeHashSet._handle->Count)
{
ref var entry = ref _nativeHashSet._handle->Entries[_index++];
if (entry.Next >= -1)
{
_current = entry.Value;
return true;
}
}
_index = _nativeHashSet._handle->Count + 1;
_current = default;
return false;
}
/// <summary>
/// Current
/// </summary>
public T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _current;
}
}
}
}

View File

@@ -0,0 +1,676 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native list
/// </summary>
/// <typeparam name="T">Type</typeparam>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeList<T> : IDisposable, IEquatable<NativeList<T>> where T : unmanaged, IEquatable<T>
{
/// <summary>
/// Handle
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct NativeListHandle
{
/// <summary>
/// Array
/// </summary>
public T* Array;
/// <summary>
/// Length
/// </summary>
public int Length;
/// <summary>
/// Size
/// </summary>
public int Size;
/// <summary>
/// Version
/// </summary>
public int Version;
}
/// <summary>
/// Handle
/// </summary>
private readonly NativeListHandle* _handle;
/// <summary>
/// Structure
/// </summary>
/// <param name="capacity">Capacity</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeList(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "MustBeNonNegative");
if (capacity < 4)
capacity = 4;
_handle = (NativeListHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeListHandle));
_handle->Array = (T*)NativeMemoryAllocator.Alloc((uint)(capacity * sizeof(T)));
_handle->Length = capacity;
_handle->Size = 0;
_handle->Version = 0;
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle != null;
/// <summary>
/// Is empty
/// </summary>
public bool IsEmpty => _handle->Size == 0;
/// <summary>
/// Get or set value
/// </summary>
/// <param name="index">Index</param>
public ref T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _handle->Array[index];
}
/// <summary>
/// Get or set value
/// </summary>
/// <param name="index">Index</param>
public ref T this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _handle->Array[index];
}
/// <summary>
/// Count
/// </summary>
public int Count => _handle->Size;
/// <summary>
/// Capacity
/// </summary>
public int Capacity
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _handle->Length;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
if (value < _handle->Size)
throw new ArgumentOutOfRangeException(nameof(Capacity), value, "SmallCapacity");
if (value != _handle->Length)
{
if (value > 0)
{
var newItems = (T*)NativeMemoryAllocator.Alloc((uint)(value * sizeof(T)));
if (_handle->Size > 0)
Unsafe.CopyBlockUnaligned(newItems, _handle->Array, (uint)(_handle->Size * sizeof(T)));
NativeMemoryAllocator.Free(_handle->Array);
_handle->Array = newItems;
_handle->Length = value;
}
else
{
NativeMemoryAllocator.Free(_handle->Array);
_handle->Array = (T*)NativeMemoryAllocator.Alloc(0);
_handle->Length = 0;
}
}
}
}
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeList<T> other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeList<T> nativeList && nativeList == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"NativeList<{typeof(T).Name}>";
/// <summary>
/// As span
/// </summary>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Span<T>(NativeList<T> nativeList) => nativeList.AsSpan();
/// <summary>
/// As readOnly span
/// </summary>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(NativeList<T> nativeList) => nativeList.AsReadOnlySpan();
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeList<T> left, NativeList<T> right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeList<T> left, NativeList<T> right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_handle == null)
return;
NativeMemoryAllocator.Free(_handle->Array);
NativeMemoryAllocator.Free(_handle);
}
/// <summary>
/// As span
/// </summary>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref *_handle->Array, _handle->Length);
/// <summary>
/// As span
/// </summary>
/// <param name="length">Length</param>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan(int length) => MemoryMarshal.CreateSpan(ref *_handle->Array, length);
/// <summary>
/// As span
/// </summary>
/// <param name="start">Start</param>
/// <param name="length">Length</param>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan(int start, int length) => MemoryMarshal.CreateSpan(ref *(_handle->Array + start), length);
/// <summary>
/// As readOnly span
/// </summary>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> AsReadOnlySpan() => MemoryMarshal.CreateReadOnlySpan(ref *_handle->Array, _handle->Length);
/// <summary>
/// As readOnly span
/// </summary>
/// <param name="length">Length</param>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> AsReadOnlySpan(int length) => MemoryMarshal.CreateReadOnlySpan(ref *_handle->Array, length);
/// <summary>
/// As readOnly span
/// </summary>
/// <param name="start">Start</param>
/// <param name="length">Length</param>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> AsReadOnlySpan(int start, int length) => MemoryMarshal.CreateReadOnlySpan(ref *(_handle->Array + start), length);
/// <summary>
/// Clear
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
_handle->Version++;
_handle->Size = 0;
}
/// <summary>
/// Add
/// </summary>
/// <param name="item">Item</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(in T item)
{
_handle->Version++;
var size = _handle->Size;
if ((uint)size < (uint)_handle->Length)
{
_handle->Size = size + 1;
_handle->Array[size] = item;
}
else
{
Grow(size + 1);
_handle->Size = size + 1;
_handle->Array[size] = item;
}
}
/// <summary>
/// Add range
/// </summary>
/// <param name="collection">Collection</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddRange(in NativeList<T> collection)
{
var count = collection._handle->Size;
if (count > 0)
{
if (_handle->Length - _handle->Size < count)
Grow(checked(_handle->Size + count));
Unsafe.CopyBlockUnaligned(_handle->Array + _handle->Size, collection._handle->Array, (uint)(collection._handle->Size * sizeof(T)));
_handle->Size += count;
_handle->Version++;
}
}
/// <summary>
/// Insert
/// </summary>
/// <param name="index">Index</param>
/// <param name="item">Item</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Insert(int index, in T item)
{
if ((uint)index > (uint)_handle->Size)
throw new ArgumentOutOfRangeException(nameof(index), index, "ListInsert");
if (_handle->Size == _handle->Length)
Grow(_handle->Size + 1);
if (index < _handle->Size)
Unsafe.CopyBlockUnaligned(_handle->Array + (index + 1), _handle->Array + index, (uint)((_handle->Size - index) * sizeof(T)));
_handle->Array[index] = item;
_handle->Size++;
_handle->Version++;
}
/// <summary>
/// Insert
/// </summary>
/// <param name="index">Index</param>
/// <param name="collection">Collection</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void InsertRange(int index, in NativeList<T> collection)
{
if ((uint)index > (uint)_handle->Size)
throw new ArgumentOutOfRangeException(nameof(index), index, "IndexMustBeLessOrEqual");
var count = collection._handle->Size;
if (count > 0)
{
if (_handle->Length - _handle->Size < count)
Grow(checked(_handle->Size + count));
if (index < _handle->Size)
Unsafe.CopyBlockUnaligned(_handle->Array + index + count, _handle->Array + index, (uint)((_handle->Size - index) * sizeof(T)));
if (this == collection)
{
Unsafe.CopyBlockUnaligned(_handle->Array + index, _handle->Array, (uint)(index * sizeof(T)));
Unsafe.CopyBlockUnaligned(_handle->Array + index * 2, _handle->Array + index + count, (uint)((_handle->Size - index) * sizeof(T)));
}
else
{
Unsafe.CopyBlockUnaligned(_handle->Array + index, collection._handle->Array, (uint)(collection._handle->Size * sizeof(T)));
}
_handle->Size += count;
_handle->Version++;
}
}
/// <summary>
/// Remove
/// </summary>
/// <param name="item">Item</param>
/// <returns>Removed</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(in T item)
{
var index = IndexOf(item);
if (index >= 0)
{
RemoveAt(index);
return true;
}
return false;
}
/// <summary>
/// Remove at
/// </summary>
/// <param name="index">Index</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAt(int index)
{
if ((uint)index >= (uint)_handle->Size)
throw new ArgumentOutOfRangeException(nameof(index), index, "IndexMustBeLess");
_handle->Size--;
if (index < _handle->Size)
Unsafe.CopyBlockUnaligned(_handle->Array + index, _handle->Array + (index + 1), (uint)((_handle->Size - index) * sizeof(T)));
_handle->Version++;
}
/// <summary>
/// Remove range
/// </summary>
/// <param name="index">Index</param>
/// <param name="count">Count</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveRange(int index, int count)
{
if (index < 0)
throw new ArgumentOutOfRangeException(nameof(index), index, "NeedNonNegNum");
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count), count, "NeedNonNegNum");
var offset = _handle->Size - index;
if (offset < count)
throw new ArgumentOutOfRangeException(offset.ToString(), "InvalidOffLen");
if (count > 0)
{
_handle->Size -= count;
if (index < _handle->Size)
Unsafe.CopyBlockUnaligned(_handle->Array + index, _handle->Array + (index + count), (uint)((_handle->Size - index) * sizeof(T)));
_handle->Version++;
}
}
/// <summary>
/// Reverse
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reverse()
{
if (_handle->Size > 1)
MemoryMarshal.CreateSpan(ref *_handle->Array, _handle->Size).Reverse();
_handle->Version++;
}
/// <summary>
/// Reverse
/// </summary>
/// <param name="index">Index</param>
/// <param name="count">Count</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reverse(int index, int count)
{
if (index < 0)
throw new ArgumentOutOfRangeException(nameof(index), index, "NeedNonNegNum");
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count), count, "NeedNonNegNum");
var offset = _handle->Size - index;
if (offset < count)
throw new ArgumentOutOfRangeException(offset.ToString(), "InvalidOffLen");
if (count > 1)
MemoryMarshal.CreateSpan(ref *(_handle->Array + index), count).Reverse();
_handle->Version++;
}
/// <summary>
/// Contains
/// </summary>
/// <param name="item">Item</param>
/// <returns>Contains</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(in T item) => _handle->Size != 0 && IndexOf(item) >= 0;
/// <summary>
/// Ensure capacity
/// </summary>
/// <param name="capacity">Capacity</param>
/// <returns>New capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int EnsureCapacity(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "MustBeNonNegative");
if (_handle->Length < capacity)
Grow(capacity);
return _handle->Length;
}
/// <summary>
/// Trim excess
/// </summary>
/// <returns>New capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int TrimExcess()
{
var threshold = (int)(_handle->Length * 0.9);
if (_handle->Size < threshold)
Capacity = _handle->Size;
return _handle->Length;
}
/// <summary>
/// Grow
/// </summary>
/// <param name="capacity">Capacity</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Grow(int capacity)
{
var newCapacity = 2 * _handle->Length;
if ((uint)newCapacity > 2147483591)
newCapacity = 2147483591;
var expected = _handle->Length + 4;
newCapacity = newCapacity > expected ? newCapacity : expected;
if (newCapacity < capacity)
newCapacity = capacity;
Capacity = newCapacity;
}
/// <summary>
/// Index of
/// </summary>
/// <param name="item">Item</param>
/// <returns>Index</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOf(in T item) => _handle->Size == 0 ? -1 : MemoryMarshal.CreateReadOnlySpan(ref *_handle->Array, _handle->Size).IndexOf(item);
/// <summary>
/// Index of
/// </summary>
/// <param name="item">Item</param>
/// <param name="index">Index</param>
/// <returns>Index</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOf(in T item, int index)
{
if (_handle->Size == 0)
return -1;
if (index < 0)
throw new ArgumentOutOfRangeException(nameof(index), index, "NeedNonNegNum");
if (index > _handle->Size)
throw new ArgumentOutOfRangeException(nameof(index), index, "IndexMustBeLessOrEqual");
return MemoryMarshal.CreateReadOnlySpan(ref *(_handle->Array + index), _handle->Size - index).IndexOf(item);
}
/// <summary>
/// Index of
/// </summary>
/// <param name="item">Item</param>
/// <param name="index">Index</param>
/// <param name="count">Count</param>
/// <returns>Index</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOf(in T item, int index, int count)
{
if (_handle->Size == 0)
return -1;
if (index < 0)
throw new ArgumentOutOfRangeException(nameof(index), index, "NeedNonNegNum");
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count), count, "NeedNonNegNum");
if (index > _handle->Size)
throw new ArgumentOutOfRangeException(nameof(index), index, "IndexMustBeLessOrEqual");
if (index > _handle->Size - count)
throw new ArgumentOutOfRangeException(nameof(count), count, "BiggerThanCollection");
return MemoryMarshal.CreateReadOnlySpan(ref *(_handle->Array + index), count).IndexOf(item);
}
/// <summary>
/// Last index of
/// </summary>
/// <param name="item">Item</param>
/// <returns>Index</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int LastIndexOf(in T item) => _handle->Size == 0 ? -1 : MemoryMarshal.CreateReadOnlySpan(ref *(_handle->Array + (_handle->Size - 1)), _handle->Size).LastIndexOf(item);
/// <summary>
/// Last index of
/// </summary>
/// <param name="item">Item</param>
/// <param name="index">Index</param>
/// <returns>Index</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int LastIndexOf(in T item, int index)
{
if (_handle->Size == 0)
return -1;
if (index < 0)
throw new ArgumentOutOfRangeException(nameof(index), index, "NeedNonNegNum");
if (index >= _handle->Size)
throw new ArgumentOutOfRangeException(nameof(index), index, "IndexMustBeLess");
return MemoryMarshal.CreateReadOnlySpan(ref *(_handle->Array + index), index + 1).LastIndexOf(item);
}
/// <summary>
/// Last index of
/// </summary>
/// <param name="item">Item</param>
/// <param name="index">Index</param>
/// <param name="count">Count</param>
/// <returns>Index</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int LastIndexOf(in T item, int index, int count)
{
if (_handle->Size == 0)
return -1;
if (index < 0)
throw new ArgumentOutOfRangeException(nameof(index), index, "NeedNonNegNum");
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count), count, "NeedNonNegNum");
if (index >= _handle->Size)
throw new ArgumentOutOfRangeException(nameof(index), index, "BiggerThanCollection");
if (count > index + 1)
throw new ArgumentOutOfRangeException(nameof(count), count, "BiggerThanCollection");
return MemoryMarshal.CreateReadOnlySpan(ref *(_handle->Array + index), count).LastIndexOf(item);
}
/// <summary>
/// Empty
/// </summary>
public static NativeList<T> Empty => new();
/// <summary>
/// Get enumerator
/// </summary>
/// <returns>Enumerator</returns>
public Enumerator GetEnumerator() => new(this);
/// <summary>
/// Enumerator
/// </summary>
public struct Enumerator
{
/// <summary>
/// NativeList
/// </summary>
private readonly NativeList<T> _nativeList;
/// <summary>
/// Version
/// </summary>
private readonly int _version;
/// <summary>
/// Index
/// </summary>
private int _index;
/// <summary>
/// Current
/// </summary>
private T _current;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeList">NativeList</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(in NativeList<T> nativeList)
{
_nativeList = nativeList;
_index = 0;
_version = nativeList._handle->Version;
_current = default;
}
/// <summary>
/// Move next
/// </summary>
/// <returns>Moved</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
var localList = _nativeList;
if (_version == localList._handle->Version && (uint)_index < (uint)localList._handle->Size)
{
_current = localList._handle->Array[_index];
_index++;
return true;
}
if (_version != _nativeList._handle->Version)
throw new InvalidOperationException("EnumFailedVersion");
_index = _nativeList._handle->Size + 1;
_current = default;
return false;
}
/// <summary>
/// Current
/// </summary>
public T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _current;
}
}
}
}

View File

@@ -0,0 +1,233 @@
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if NET7_0_OR_GREATER
using System.Runtime.Intrinsics;
#endif
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native memory allocator
/// </summary>
public static unsafe class NativeMemoryAllocator
{
/// <summary>
/// Alloc
/// </summary>
/// <param name="byteCount">Byte count</param>
/// <returns>Memory</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void* Alloc(uint byteCount)
{
#if NET6_0_OR_GREATER
return NativeMemory.Alloc(byteCount);
#else
return (void*)Marshal.AllocHGlobal((nint)byteCount);
#endif
}
/// <summary>
/// Alloc zeroed
/// </summary>
/// <param name="byteCount">Byte count</param>
/// <returns>Memory</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void* AllocZeroed(uint byteCount)
{
#if NET6_0_OR_GREATER
return NativeMemory.AllocZeroed(byteCount, 1);
#else
var ptr = (void*)Marshal.AllocHGlobal((nint)byteCount);
Unsafe.InitBlockUnaligned(ptr, 0, byteCount);
return ptr;
#endif
}
/// <summary>
/// Free
/// </summary>
/// <param name="ptr">Pointer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Free(void* ptr)
{
#if NET6_0_OR_GREATER
NativeMemory.Free(ptr);
#else
Marshal.FreeHGlobal((nint)ptr);
#endif
}
/// <summary>
/// Copy
/// </summary>
/// <param name="destination">Destination</param>
/// <param name="source">Source</param>
/// <param name="byteCount">Byte count</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Copy(void* destination, void* source, uint byteCount) => Unsafe.CopyBlockUnaligned(destination, source, byteCount);
/// <summary>
/// Move
/// </summary>
/// <param name="destination">Destination</param>
/// <param name="source">Source</param>
/// <param name="byteCount">Byte count</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Move(void* destination, void* source, uint byteCount) => Buffer.MemoryCopy(source, destination, byteCount, byteCount);
/// <summary>
/// Set
/// </summary>
/// <param name="startAddress">Start address</param>
/// <param name="value">Value</param>
/// <param name="byteCount">Byte count</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Set(void* startAddress, byte value, uint byteCount) => Unsafe.InitBlockUnaligned(startAddress, value, byteCount);
/// <summary>
/// Compare
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <param name="byteCount">Byte count</param>
/// <returns>Sequences equal</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Compare(void* left, void* right, uint byteCount)
{
ref var first = ref *(byte*)left;
ref var second = ref *(byte*)right;
nuint length = byteCount;
if (length >= (nuint)sizeof(nuint))
{
if (!Unsafe.AreSame(ref first, ref second))
{
#if NET7_0_OR_GREATER
if (Vector128.IsHardwareAccelerated)
{
#if NET8_0_OR_GREATER
if (Vector512.IsHardwareAccelerated && length >= (nuint)Vector512<byte>.Count)
{
nuint offset = 0;
var lengthToExamine = length - (nuint)Vector512<byte>.Count;
if (lengthToExamine != 0)
{
do
{
if (Vector512.LoadUnsafe(ref first, offset) != Vector512.LoadUnsafe(ref second, offset))
return false;
offset += (nuint)Vector512<byte>.Count;
} while (lengthToExamine > offset);
}
return Vector512.LoadUnsafe(ref first, lengthToExamine) == Vector512.LoadUnsafe(ref second, lengthToExamine);
}
#endif
if (Vector256.IsHardwareAccelerated && length >= (nuint)Vector256<byte>.Count)
{
nuint offset = 0;
var lengthToExamine = length - (nuint)Vector256<byte>.Count;
if (lengthToExamine != 0)
{
do
{
if (Vector256.LoadUnsafe(ref first, offset) != Vector256.LoadUnsafe(ref second, offset))
return false;
offset += (nuint)Vector256<byte>.Count;
} while (lengthToExamine > offset);
}
return Vector256.LoadUnsafe(ref first, lengthToExamine) == Vector256.LoadUnsafe(ref second, lengthToExamine);
}
if (length >= (nuint)Vector128<byte>.Count)
{
nuint offset = 0;
var lengthToExamine = length - (nuint)Vector128<byte>.Count;
if (lengthToExamine != 0)
{
do
{
if (Vector128.LoadUnsafe(ref first, offset) != Vector128.LoadUnsafe(ref second, offset))
return false;
offset += (nuint)Vector128<byte>.Count;
} while (lengthToExamine > offset);
}
return Vector128.LoadUnsafe(ref first, lengthToExamine) == Vector128.LoadUnsafe(ref second, lengthToExamine);
}
}
if (IntPtr.Size == 8 && Vector128.IsHardwareAccelerated)
{
var offset = length - (nuint)sizeof(nuint);
var differentBits = Unsafe.ReadUnaligned<nuint>(ref first) - Unsafe.ReadUnaligned<nuint>(ref second);
differentBits |= Unsafe.ReadUnaligned<nuint>(ref Unsafe.AddByteOffset(ref first, offset)) - Unsafe.ReadUnaligned<nuint>(ref Unsafe.AddByteOffset(ref second, offset));
return differentBits == 0;
}
else
#endif
{
nuint offset = 0;
var lengthToExamine = length - (nuint)sizeof(nuint);
if (lengthToExamine > 0)
{
do
{
#if NET7_0_OR_GREATER
if (Unsafe.ReadUnaligned<nuint>(ref Unsafe.AddByteOffset(ref first, offset)) != Unsafe.ReadUnaligned<nuint>(ref Unsafe.AddByteOffset(ref second, offset)))
#else
if (Unsafe.ReadUnaligned<nuint>(ref Unsafe.AddByteOffset(ref first, (nint)offset)) != Unsafe.ReadUnaligned<nuint>(ref Unsafe.AddByteOffset(ref second, (nint)offset)))
#endif
return false;
offset += (nuint)sizeof(nuint);
} while (lengthToExamine > offset);
}
#if NET7_0_OR_GREATER
return Unsafe.ReadUnaligned<nuint>(ref Unsafe.AddByteOffset(ref first, lengthToExamine)) == Unsafe.ReadUnaligned<nuint>(ref Unsafe.AddByteOffset(ref second, lengthToExamine));
#else
return Unsafe.ReadUnaligned<nuint>(ref Unsafe.AddByteOffset(ref first, (nint)lengthToExamine)) == Unsafe.ReadUnaligned<nuint>(ref Unsafe.AddByteOffset(ref second, (nint)lengthToExamine));
#endif
}
}
return true;
}
if (length < sizeof(uint) || IntPtr.Size != 8)
{
uint differentBits = 0;
var offset = length & 2;
if (offset != 0)
{
differentBits = Unsafe.ReadUnaligned<ushort>(ref first);
differentBits -= Unsafe.ReadUnaligned<ushort>(ref second);
}
if ((length & 1) != 0)
#if NET7_0_OR_GREATER
differentBits |= Unsafe.AddByteOffset(ref first, offset) - (uint)Unsafe.AddByteOffset(ref second, offset);
#else
differentBits |= Unsafe.AddByteOffset(ref first, (nint)offset) - (uint)Unsafe.AddByteOffset(ref second, (nint)offset);
#endif
return differentBits == 0;
}
else
{
var offset = length - sizeof(uint);
var differentBits = Unsafe.ReadUnaligned<uint>(ref first) - Unsafe.ReadUnaligned<uint>(ref second);
#if NET7_0_OR_GREATER
differentBits |= Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref first, offset)) - Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref second, offset));
#else
differentBits |= Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref first, (nint)offset)) - Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref second, (nint)offset));
#endif
return differentBits == 0;
}
}
}
}

View File

@@ -0,0 +1,329 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native memory array
/// </summary>
/// <typeparam name="T">Type</typeparam>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeMemoryArray<T> : IDisposable, IEquatable<NativeMemoryArray<T>> where T : unmanaged
{
/// <summary>
/// Array
/// </summary>
private readonly T* _array;
/// <summary>
/// Length
/// </summary>
private readonly int _length;
/// <summary>
/// Structure
/// </summary>
/// <param name="length">Length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeMemoryArray(int length)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
_array = (T*)NativeMemoryAllocator.Alloc((uint)(length * sizeof(T)));
_length = length;
}
/// <summary>
/// Structure
/// </summary>
/// <param name="length">Length</param>
/// <param name="zeroed">Zeroed</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeMemoryArray(int length, bool zeroed)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
_array = zeroed ? (T*)NativeMemoryAllocator.AllocZeroed((uint)(length * sizeof(T))) : (T*)NativeMemoryAllocator.Alloc((uint)(length * sizeof(T)));
_length = length;
}
/// <summary>
/// Structure
/// </summary>
/// <param name="array">Array</param>
/// <param name="length">Length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeMemoryArray(T* array, int length)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
_array = array;
_length = length;
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _array != null;
/// <summary>
/// Is empty
/// </summary>
public bool IsEmpty => _length == 0;
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public T* this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array + index;
}
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public T* this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array + index;
}
/// <summary>
/// Array
/// </summary>
public T* Array => _array;
/// <summary>
/// Length
/// </summary>
public int Length => _length;
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeMemoryArray<T> other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeMemoryArray<T> nativeMemoryArray && nativeMemoryArray == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_array;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"NativeMemoryArray<{typeof(T).Name}>[{_length}]";
/// <summary>
/// As span
/// </summary>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Span<T>(NativeMemoryArray<T> nativeMemoryArray) => nativeMemoryArray.AsSpan();
/// <summary>
/// As readOnly span
/// </summary>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(NativeMemoryArray<T> nativeMemoryArray) => nativeMemoryArray.AsReadOnlySpan();
/// <summary>
/// As native array
/// </summary>
/// <param name="nativeMemoryArray">Native memory array</param>
/// <returns>NativeArray</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NativeArray<T>(NativeMemoryArray<T> nativeMemoryArray) => new(nativeMemoryArray._array, nativeMemoryArray._length);
/// <summary>
/// As native memory array
/// </summary>
/// <param name="nativeArray">Native array</param>
/// <returns>NativeMemoryArray</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NativeMemoryArray<T>(NativeArray<T> nativeArray) => new(nativeArray.Array, nativeArray.Length);
/// <summary>
/// As native array segment
/// </summary>
/// <param name="nativeMemoryArray">Native memory array</param>
/// <returns>NativeArraySegment</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NativeArraySegment<T>(NativeMemoryArray<T> nativeMemoryArray) => new(nativeMemoryArray._array, nativeMemoryArray._length);
/// <summary>
/// As native memory array
/// </summary>
/// <param name="nativeArraySegment">Native array segment</param>
/// <returns>NativeMemoryArray</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NativeMemoryArray<T>(NativeArraySegment<T> nativeArraySegment) => new(nativeArraySegment.Array, nativeArraySegment.Offset + nativeArraySegment.Count);
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeMemoryArray<T> left, NativeMemoryArray<T> right) => left._length == right._length && left._array == right._array;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeMemoryArray<T> left, NativeMemoryArray<T> right) => left._length != right._length || left._array != right._array;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_array == null)
return;
NativeMemoryAllocator.Free(_array);
}
/// <summary>
/// Clear
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() => Unsafe.InitBlockUnaligned(_array, 0, (uint)(_length * sizeof(T)));
/// <summary>
/// As span
/// </summary>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref *_array, _length);
/// <summary>
/// As span
/// </summary>
/// <param name="length">Length</param>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan(int length) => MemoryMarshal.CreateSpan(ref *_array, length);
/// <summary>
/// As span
/// </summary>
/// <param name="start">Start</param>
/// <param name="length">Length</param>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan(int start, int length) => MemoryMarshal.CreateSpan(ref *(_array + start), length);
/// <summary>
/// As readOnly span
/// </summary>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> AsReadOnlySpan() => MemoryMarshal.CreateReadOnlySpan(ref *_array, _length);
/// <summary>
/// As readOnly span
/// </summary>
/// <param name="length">Length</param>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> AsReadOnlySpan(int length) => MemoryMarshal.CreateReadOnlySpan(ref *_array, length);
/// <summary>
/// As readOnly span
/// </summary>
/// <param name="start">Start</param>
/// <param name="length">Length</param>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> AsReadOnlySpan(int start, int length) => MemoryMarshal.CreateReadOnlySpan(ref *(_array + start), length);
/// <summary>
/// Empty
/// </summary>
public static NativeMemoryArray<T> Empty => new();
/// <summary>
/// Get enumerator
/// </summary>
/// <returns>Enumerator</returns>
public Enumerator GetEnumerator() => new(this);
/// <summary>
/// Enumerator
/// </summary>
public ref struct Enumerator
{
/// <summary>
/// NativeMemoryArray
/// </summary>
private readonly NativeMemoryArray<T> _nativeMemoryArray;
/// <summary>
/// Index
/// </summary>
private int _index;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeMemoryArray">NativeMemoryArray</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(NativeMemoryArray<T> nativeMemoryArray)
{
_nativeMemoryArray = nativeMemoryArray;
_index = -1;
}
/// <summary>
/// Move next
/// </summary>
/// <returns>Moved</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
var index = _index + 1;
if (index < _nativeMemoryArray._length)
{
_index = index;
return true;
}
return false;
}
/// <summary>
/// Current
/// </summary>
public T* Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _nativeMemoryArray[_index];
}
}
}
}

View File

@@ -0,0 +1,199 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native memory bucket
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeMemoryBucket : IDisposable, IEquatable<NativeMemoryBucket>
{
/// <summary>
/// Handle
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct NativeMemoryBucketHandle
{
/// <summary>
/// Size
/// </summary>
public int Size;
/// <summary>
/// Length
/// </summary>
public int Length;
/// <summary>
/// Array
/// </summary>
public void** Array;
/// <summary>
/// Index
/// </summary>
public int Index;
/// <summary>
/// Memory pool
/// </summary>
public NativeMemoryPool MemoryPool;
}
/// <summary>
/// Handle
/// </summary>
private readonly NativeMemoryBucketHandle* _handle;
/// <summary>
/// Structure
/// </summary>
/// <param name="size">Size</param>
/// <param name="length">Length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeMemoryBucket(int size, int length)
{
if (size <= 0)
throw new ArgumentOutOfRangeException(nameof(size), size, "MustBePositive");
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
_handle = (NativeMemoryBucketHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeMemoryBucketHandle));
_handle->Size = size;
_handle->Length = length;
_handle->Array = (void**)NativeMemoryAllocator.AllocZeroed((uint)(size * sizeof(void*)));
_handle->Index = 0;
_handle->MemoryPool = new NativeMemoryPool(size, length, 0);
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle != null;
/// <summary>
/// Is empty
/// </summary>
public bool IsEmpty => _handle->Index == 0;
/// <summary>
/// Is full
/// </summary>
public bool IsFull => _handle->Index == _handle->Size;
/// <summary>
/// Size
/// </summary>
public int Size => _handle->Size;
/// <summary>
/// Length
/// </summary>
public int Length => _handle->Length;
/// <summary>
/// Count
/// </summary>
public int Count => _handle->Index;
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeMemoryBucket other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeMemoryBucket nativeMemoryBucket && nativeMemoryBucket == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => "NativeMemoryBucket";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeMemoryBucket left, NativeMemoryBucket right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeMemoryBucket left, NativeMemoryBucket right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_handle == null)
return;
NativeMemoryAllocator.Free(_handle->Array);
_handle->MemoryPool.Dispose();
NativeMemoryAllocator.Free(_handle);
}
/// <summary>
/// Rent buffer
/// </summary>
/// <returns>Buffer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void* Rent()
{
void* buffer = null;
if (_handle->Index < _handle->Size)
{
buffer = _handle->Array[_handle->Index];
_handle->Array[_handle->Index++] = null;
}
if (buffer == null)
buffer = _handle->MemoryPool.Rent();
return buffer;
}
/// <summary>
/// Return buffer
/// </summary>
/// <param name="ptr">Pointer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Return(void* ptr)
{
if (_handle->Index != 0)
_handle->Array[--_handle->Index] = ptr;
else
_handle->MemoryPool.Return(ptr);
}
/// <summary>
/// Empty
/// </summary>
public static NativeMemoryBucket Empty => new();
}
}

View File

@@ -0,0 +1,388 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native memory pool
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeMemoryPool : IDisposable, IEquatable<NativeMemoryPool>
{
/// <summary>
/// Handle
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct NativeMemoryPoolHandle
{
/// <summary>
/// Slab
/// </summary>
public NativeMemorySlab* Slab;
/// <summary>
/// Free slab
/// </summary>
public NativeMemorySlab* FreeSlab;
/// <summary>
/// Slabs
/// </summary>
public int Slabs;
/// <summary>
/// Free slabs
/// </summary>
public int FreeSlabs;
/// <summary>
/// Max free slabs
/// </summary>
public int MaxFreeSlabs;
/// <summary>
/// Size
/// </summary>
public int Size;
/// <summary>
/// Length
/// </summary>
public int Length;
}
/// <summary>
/// Slab
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct NativeMemorySlab
{
/// <summary>
/// Next
/// </summary>
public NativeMemorySlab* Next;
/// <summary>
/// Previous
/// </summary>
public NativeMemorySlab* Previous;
/// <summary>
/// Node
/// </summary>
public NativeMemoryNode* Node;
/// <summary>
/// Count
/// </summary>
public int Count;
}
/// <summary>
/// Node
/// </summary>
[StructLayout(LayoutKind.Explicit)]
private struct NativeMemoryNode
{
/// <summary>
/// Slab
/// </summary>
[FieldOffset(0)] public NativeMemorySlab* Slab;
/// <summary>
/// Next
/// </summary>
[FieldOffset(0)] public NativeMemoryNode* Next;
}
/// <summary>
/// Handle
/// </summary>
private readonly NativeMemoryPoolHandle* _handle;
/// <summary>
/// Structure
/// </summary>
/// <param name="size">Size</param>
/// <param name="length">Length</param>
/// <param name="maxFreeSlabs">Max free slabs</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeMemoryPool(int size, int length, int maxFreeSlabs)
{
if (size <= 0)
throw new ArgumentOutOfRangeException(nameof(size), size, "MustBePositive");
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
if (maxFreeSlabs < 0)
throw new ArgumentOutOfRangeException(nameof(maxFreeSlabs), maxFreeSlabs, "MustBeNonNegative");
var nodeSize = sizeof(NativeMemoryNode) + length;
var array = (byte*)NativeMemoryAllocator.Alloc((uint)(sizeof(NativeMemorySlab) + size * nodeSize));
var slab = (NativeMemorySlab*)array;
slab->Next = slab;
slab->Previous = slab;
array += sizeof(NativeMemorySlab);
NativeMemoryNode* next = null;
for (var i = size - 1; i >= 0; --i)
{
var node = (NativeMemoryNode*)(array + i * nodeSize);
node->Next = next;
next = node;
}
slab->Node = next;
slab->Count = size;
_handle = (NativeMemoryPoolHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeMemoryPoolHandle));
_handle->Slab = slab;
_handle->FreeSlab = null;
_handle->Slabs = 1;
_handle->FreeSlabs = 0;
_handle->MaxFreeSlabs = maxFreeSlabs;
_handle->Size = size;
_handle->Length = length;
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle != null;
/// <summary>
/// Slabs
/// </summary>
public int Slabs => _handle->Slabs;
/// <summary>
/// Free slabs
/// </summary>
public int FreeSlabs => _handle->FreeSlabs;
/// <summary>
/// Max free slabs
/// </summary>
public int MaxFreeSlabs => _handle->MaxFreeSlabs;
/// <summary>
/// Size
/// </summary>
public int Size => _handle->Size;
/// <summary>
/// Length
/// </summary>
public int Length => _handle->Length;
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeMemoryPool other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeMemoryPool nativeMemoryPool && nativeMemoryPool == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => "NativeMemoryPool";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeMemoryPool left, NativeMemoryPool right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeMemoryPool left, NativeMemoryPool right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_handle == null)
return;
var node = _handle->Slab;
while (_handle->Slabs > 0)
{
_handle->Slabs--;
var temp = node;
node = node->Next;
NativeMemoryAllocator.Free(temp);
}
node = _handle->FreeSlab;
while (_handle->FreeSlabs > 0)
{
_handle->FreeSlabs--;
var temp = node;
node = node->Next;
NativeMemoryAllocator.Free(temp);
}
NativeMemoryAllocator.Free(_handle);
}
/// <summary>
/// Rent buffer
/// </summary>
/// <returns>Buffer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void* Rent()
{
NativeMemoryNode* node;
var slab = _handle->Slab;
if (slab->Count == 0)
{
_handle->Slab = slab->Next;
slab = _handle->Slab;
if (slab->Count == 0)
{
var size = _handle->Size;
if (_handle->FreeSlabs == 0)
{
var nodeSize = sizeof(NativeMemoryNode) + _handle->Length;
var array = (byte*)NativeMemoryAllocator.Alloc((uint)(sizeof(NativeMemorySlab) + size * nodeSize));
slab = (NativeMemorySlab*)array;
array += sizeof(NativeMemorySlab);
NativeMemoryNode* next = null;
for (var i = size - 1; i >= 0; --i)
{
node = (NativeMemoryNode*)(array + i * nodeSize);
node->Next = next;
next = node;
}
slab->Node = next;
}
else
{
slab = _handle->FreeSlab;
_handle->FreeSlab = slab->Next;
_handle->FreeSlabs--;
}
slab->Next = _handle->Slab;
slab->Previous = _handle->Slab->Previous;
slab->Count = size;
_handle->Slab->Previous->Next = slab;
_handle->Slab->Previous = slab;
_handle->Slab = slab;
_handle->Slabs++;
}
}
node = slab->Node;
slab->Node = node->Next;
node->Slab = slab;
slab->Count--;
return (byte*)node + sizeof(NativeMemoryNode);
}
/// <summary>
/// Return buffer
/// </summary>
/// <param name="ptr">Pointer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Return(void* ptr)
{
var node = (NativeMemoryNode*)((byte*)ptr - sizeof(NativeMemoryNode));
var slab = node->Slab;
slab->Count++;
if (slab->Count == _handle->Size && slab != _handle->Slab)
{
slab->Previous->Next = slab->Next;
slab->Next->Previous = slab->Previous;
if (_handle->FreeSlabs == _handle->MaxFreeSlabs)
{
NativeMemoryAllocator.Free(slab);
}
else
{
node->Next = slab->Node;
slab->Node = node;
slab->Next = _handle->FreeSlab;
_handle->FreeSlab = slab;
_handle->FreeSlabs++;
}
_handle->Slabs--;
return;
}
node->Next = slab->Node;
slab->Node = node;
}
/// <summary>
/// Trim excess
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void TrimExcess()
{
var node = _handle->FreeSlab;
while (_handle->FreeSlabs > 0)
{
_handle->FreeSlabs--;
var temp = node;
node = node->Next;
NativeMemoryAllocator.Free(temp);
}
_handle->FreeSlab = node;
}
/// <summary>
/// Trim excess
/// </summary>
/// <param name="capacity">Remaining free slabs</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void TrimExcess(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "MustBeNonNegative");
var node = _handle->FreeSlab;
while (_handle->FreeSlabs > capacity)
{
_handle->FreeSlabs--;
var temp = node;
node = node->Next;
NativeMemoryAllocator.Free(temp);
}
_handle->FreeSlab = node;
}
/// <summary>
/// Empty
/// </summary>
public static NativeMemoryPool Empty => new();
}
}

View File

@@ -0,0 +1,274 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native memory reader
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public unsafe ref struct NativeMemoryReader
{
/// <summary>
/// Array
/// </summary>
public readonly byte* Array;
/// <summary>
/// Length
/// </summary>
public readonly int Length;
/// <summary>
/// Position
/// </summary>
public int Position;
/// <summary>
/// Structure
/// </summary>
/// <param name="array">Array</param>
/// <param name="length">Length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeMemoryReader(byte* array, int length)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
Array = array;
Length = length;
Position = 0;
}
/// <summary>
/// Remaining
/// </summary>
public int Remaining => Length - Position;
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public byte* this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Array + index;
}
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public byte* this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Array + index;
}
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeMemoryReader other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => throw new NotSupportedException("Cannot call Equals on NativeMemoryReader");
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => HashCode.Combine((int)(nint)Array, Length, Position);
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => "NativeMemoryReader";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeMemoryReader left, NativeMemoryReader right) => left.Array == right.Array && left.Length == right.Length && left.Position == right.Position;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeMemoryReader left, NativeMemoryReader right) => left.Array != right.Array || left.Length != right.Length || left.Position != right.Position;
/// <summary>
/// Advance
/// </summary>
/// <param name="count">Count</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Advance(int count)
{
var newPosition = Position + count;
if (newPosition < 0 || newPosition > Length)
throw new ArgumentOutOfRangeException(nameof(count), "Cannot advance past the end of the buffer.");
Position = newPosition;
}
/// <summary>
/// Try advance
/// </summary>
/// <param name="count">Count</param>
/// <returns>Advanced</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryAdvance(int count)
{
var newPosition = Position + count;
if (newPosition < 0 || newPosition > Length)
return false;
Position = newPosition;
return true;
}
/// <summary>
/// Read
/// </summary>
/// <param name="obj">object</param>
/// <typeparam name="T">Type</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Read<T>(T* obj) where T : unmanaged
{
if (Position + sizeof(T) > Length)
throw new ArgumentOutOfRangeException(nameof(T), $"Requires size is {sizeof(T)}, but buffer length is {Remaining}.");
Unsafe.CopyBlockUnaligned(obj, Array + Position, (uint)sizeof(T));
Position += sizeof(T);
}
/// <summary>
/// Try read
/// </summary>
/// <param name="obj">object</param>
/// <typeparam name="T">Type</typeparam>
/// <returns>Read</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryRead<T>(T* obj) where T : unmanaged
{
if (Position + sizeof(T) > Length)
return false;
Unsafe.CopyBlockUnaligned(obj, Array + Position, (uint)sizeof(T));
Position += sizeof(T);
return true;
}
/// <summary>
/// Read
/// </summary>
/// <param name="obj">object</param>
/// <param name="count">Count</param>
/// <typeparam name="T">Type</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Read<T>(T* obj, int count) where T : unmanaged
{
count *= sizeof(T);
if (Position + count > Length)
throw new ArgumentOutOfRangeException(nameof(T), $"Requires size is {count}, but buffer length is {Remaining}.");
Unsafe.CopyBlockUnaligned(obj, Array + Position, (uint)count);
Position += count;
}
/// <summary>
/// Try read
/// </summary>
/// <param name="obj">object</param>
/// <param name="count">Count</param>
/// <typeparam name="T">Type</typeparam>
/// <returns>Read</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryRead<T>(T* obj, int count) where T : unmanaged
{
count *= sizeof(T);
if (Position + count > Length)
return false;
Unsafe.CopyBlockUnaligned(obj, Array + Position, (uint)count);
Position += count;
return true;
}
/// <summary>
/// Read
/// </summary>
/// <param name="obj">object</param>
/// <typeparam name="T">Type</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Read<T>(ref T obj) where T : unmanaged
{
if (Position + sizeof(T) > Length)
throw new ArgumentOutOfRangeException(nameof(T), $"Requires size is {sizeof(T)}, but buffer length is {Remaining}.");
obj = Unsafe.ReadUnaligned<T>(Array + Position);
Position += sizeof(T);
}
/// <summary>
/// Try read
/// </summary>
/// <param name="obj">object</param>
/// <typeparam name="T">Type</typeparam>
/// <returns>Read</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryRead<T>(ref T obj) where T : unmanaged
{
if (Position + sizeof(T) > Length)
return false;
obj = Unsafe.ReadUnaligned<T>(Array + Position);
Position += sizeof(T);
return true;
}
/// <summary>
/// Read bytes
/// </summary>
/// <param name="buffer">Buffer</param>
/// <param name="length">Length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadBytes(byte* buffer, int length)
{
if (Position + length > Length)
throw new ArgumentOutOfRangeException(nameof(length), $"Requires size is {length}, but buffer length is {Remaining}.");
Unsafe.CopyBlockUnaligned(buffer, Array + Position, (uint)length);
Position += length;
}
/// <summary>
/// Try read bytes
/// </summary>
/// <param name="buffer">Buffer</param>
/// <param name="length">Length</param>
/// <returns>Read</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryReadBytes(byte* buffer, int length)
{
if (Position + length > Length)
return false;
Unsafe.CopyBlockUnaligned(buffer, Array + Position, (uint)length);
Position += length;
return true;
}
/// <summary>
/// Empty
/// </summary>
public static NativeMemoryReader Empty => new();
}
}

View File

@@ -0,0 +1,55 @@
using System.Runtime.CompilerServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native memory reader extensions
/// </summary>
public static unsafe class NativeMemoryReaderExtensions
{
/// <summary>
/// Read
/// </summary>
/// <param name="reader">Reader</param>
/// <typeparam name="T">Type</typeparam>
/// <returns>object</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Read<T>(this ref NativeMemoryReader reader) where T : unmanaged
{
if (reader.Position + sizeof(T) > reader.Length)
throw new ArgumentOutOfRangeException(nameof(T), $"Requires size is {sizeof(T)}, but buffer length is {reader.Remaining}.");
var obj = Unsafe.ReadUnaligned<T>(reader.Array + reader.Position);
reader.Position += sizeof(T);
return obj;
}
/// <summary>
/// Try read
/// </summary>
/// <param name="reader">Reader</param>
/// <param name="obj">object</param>
/// <typeparam name="T">Type</typeparam>
/// <returns>Read</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryRead<T>(this ref NativeMemoryReader reader, out T obj) where T : unmanaged
{
if (reader.Position + sizeof(T) > reader.Length)
{
obj = default;
return false;
}
obj = Unsafe.ReadUnaligned<T>(reader.Array + reader.Position);
reader.Position += sizeof(T);
return true;
}
}
}

View File

@@ -0,0 +1,571 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
using System.IO;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native memory stream
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public unsafe struct NativeMemoryStream : IDisposable, IEquatable<NativeMemoryStream>
{
/// <summary>
/// Handle
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct NativeMemoryStreamHandle
{
/// <summary>
/// Array
/// </summary>
public byte* Array;
/// <summary>
/// Position
/// </summary>
public int Position;
/// <summary>
/// Length
/// </summary>
public int Length;
/// <summary>
/// Capacity
/// </summary>
public int Capacity;
}
/// <summary>
/// Handle
/// </summary>
private readonly NativeMemoryStreamHandle* _handle;
/// <summary>
/// Structure
/// </summary>
/// <param name="capacity">Capacity</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeMemoryStream(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "MustBeNonNegative");
if (capacity < 4)
capacity = 4;
_handle = (NativeMemoryStreamHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeMemoryStreamHandle));
_handle->Array = (byte*)NativeMemoryAllocator.Alloc((uint)capacity);
_handle->Position = 0;
_handle->Length = 0;
_handle->Capacity = capacity;
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle != null;
/// <summary>
/// Is empty
/// </summary>
public bool IsEmpty => _handle->Length == 0;
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public ref byte this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _handle->Array[index];
}
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public ref byte this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _handle->Array[index];
}
/// <summary>
/// Can read
/// </summary>
public bool CanRead => IsCreated;
/// <summary>
/// Can seek
/// </summary>
public bool CanSeek => IsCreated;
/// <summary>
/// Can write
/// </summary>
public bool CanWrite => IsCreated;
/// <summary>
/// Length
/// </summary>
public int Length
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
EnsureNotClosed();
return _handle->Length;
}
}
/// <summary>
/// Position
/// </summary>
public int Position
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
EnsureNotClosed();
return _handle->Position;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(Position), value, "MustBeNonNegative");
EnsureNotClosed();
_handle->Position = value;
}
}
/// <summary>
/// Capacity
/// </summary>
public int Capacity
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
EnsureNotClosed();
return _handle->Capacity;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
EnsureNotClosed();
if (value < _handle->Length)
throw new ArgumentOutOfRangeException(nameof(Capacity), value, "SmallCapacity");
if (value != _handle->Capacity)
{
if (value > 0)
{
var newBuffer = (byte*)NativeMemoryAllocator.Alloc((uint)value);
if (_handle->Length > 0)
Unsafe.CopyBlockUnaligned(newBuffer, _handle->Array, (uint)_handle->Length);
NativeMemoryAllocator.Free(_handle->Array);
_handle->Array = newBuffer;
}
else
{
NativeMemoryAllocator.Free(_handle->Array);
_handle->Array = (byte*)NativeMemoryAllocator.Alloc(0);
}
_handle->Capacity = value;
}
}
}
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeMemoryStream other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeMemoryStream nativeMemoryStream && nativeMemoryStream == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"NativeMemoryStream<{_handle->Length}>";
/// <summary>
/// As span
/// </summary>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Span<byte>(NativeMemoryStream nativeList) => nativeList.AsSpan();
/// <summary>
/// As readOnly span
/// </summary>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<byte>(NativeMemoryStream nativeList) => nativeList.AsReadOnlySpan();
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeMemoryStream left, NativeMemoryStream right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeMemoryStream left, NativeMemoryStream right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_handle == null)
return;
NativeMemoryAllocator.Free(_handle->Array);
NativeMemoryAllocator.Free(_handle);
}
/// <summary>
/// As span
/// </summary>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<byte> AsSpan() => MemoryMarshal.CreateSpan(ref *_handle->Array, _handle->Length);
/// <summary>
/// As span
/// </summary>
/// <param name="length">Length</param>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<byte> AsSpan(int length) => MemoryMarshal.CreateSpan(ref *_handle->Array, length);
/// <summary>
/// As span
/// </summary>
/// <param name="start">Start</param>
/// <param name="length">Length</param>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<byte> AsSpan(int start, int length) => MemoryMarshal.CreateSpan(ref *(_handle->Array + start), length);
/// <summary>
/// As readOnly span
/// </summary>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<byte> AsReadOnlySpan() => MemoryMarshal.CreateReadOnlySpan(ref *_handle->Array, _handle->Length);
/// <summary>
/// As readOnly span
/// </summary>
/// <param name="length">Length</param>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<byte> AsReadOnlySpan(int length) => MemoryMarshal.CreateReadOnlySpan(ref *_handle->Array, length);
/// <summary>
/// As readOnly span
/// </summary>
/// <param name="start">Start</param>
/// <param name="length">Length</param>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<byte> AsReadOnlySpan(int start, int length) => MemoryMarshal.CreateReadOnlySpan(ref *(_handle->Array + start), length);
/// <summary>
/// Get buffer
/// </summary>
/// <returns>Buffer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte* GetBuffer() => _handle->Array;
/// <summary>
/// Seek
/// </summary>
/// <param name="offset">Offset</param>
/// <param name="loc">Seek origin</param>
/// <returns>Position</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Seek(int offset, SeekOrigin loc)
{
if (offset > 2147483647)
throw new ArgumentOutOfRangeException(nameof(offset), offset, "StreamLength");
EnsureNotClosed();
switch (loc)
{
case SeekOrigin.Begin:
{
if (offset < 0)
throw new IOException("IO_SeekBeforeBegin");
_handle->Position = offset;
break;
}
case SeekOrigin.Current:
{
var tempPosition = unchecked(_handle->Position + offset);
if (tempPosition < 0)
throw new IOException("IO_SeekBeforeBegin");
_handle->Position = tempPosition;
break;
}
case SeekOrigin.End:
{
var tempPosition = unchecked(_handle->Length + offset);
if (tempPosition < 0)
throw new IOException("IO_SeekBeforeBegin");
_handle->Position = tempPosition;
break;
}
default:
throw new ArgumentException("InvalidSeekOrigin");
}
return _handle->Position;
}
/// <summary>
/// Set length
/// </summary>
/// <param name="length">Length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetLength(int length)
{
if (length < 0 || length > 2147483647)
throw new ArgumentOutOfRangeException(nameof(length), length, "StreamLength");
EnsureNotClosed();
var allocatedNewArray = EnsureCapacity(length);
if (!allocatedNewArray && length > _handle->Length)
Unsafe.InitBlock(_handle->Array + _handle->Length, 0, (uint)(length - _handle->Length));
_handle->Length = length;
if (_handle->Position > length)
_handle->Position = length;
}
/// <summary>
/// Read
/// </summary>
/// <param name="buffer">Buffer</param>
/// <param name="offset">Offset</param>
/// <param name="count">Count</param>
/// <returns>Bytes</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Read(byte* buffer, int offset, int count)
{
EnsureNotClosed();
var n = _handle->Length - _handle->Position;
if (n > count)
n = count;
if (n <= 0)
return 0;
if (n <= 8)
{
var byteCount = n;
while (--byteCount >= 0)
buffer[offset + byteCount] = _handle->Array[_handle->Position + byteCount];
}
else
{
Unsafe.CopyBlockUnaligned(buffer + offset, _handle->Array + _handle->Position, (uint)n);
}
_handle->Position += n;
return n;
}
/// <summary>
/// Read
/// </summary>
/// <param name="buffer">Buffer</param>
/// <returns>Bytes</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Read(Span<byte> buffer)
{
EnsureNotClosed();
var size = _handle->Length - _handle->Position;
var n = size < buffer.Length ? size : buffer.Length;
if (n <= 0)
return 0;
Unsafe.CopyBlockUnaligned(ref buffer[0], ref *(_handle->Array + _handle->Position), (uint)n);
_handle->Position += n;
return n;
}
/// <summary>
/// Read
/// </summary>
/// <returns>Byte</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ReadByte()
{
EnsureNotClosed();
return _handle->Position >= _handle->Length ? -1 : _handle->Array[_handle->Position++];
}
/// <summary>
/// Write
/// </summary>
/// <param name="buffer">Buffer</param>
/// <param name="offset">Offset</param>
/// <param name="count">Count</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(byte* buffer, int offset, int count)
{
EnsureNotClosed();
var i = _handle->Position + count;
if (i < 0)
throw new IOException("IO_StreamTooLong");
if (i > _handle->Length)
{
var mustZero = _handle->Position > _handle->Length;
if (i > _handle->Capacity)
{
var allocatedNewArray = EnsureCapacity(i);
if (allocatedNewArray)
mustZero = false;
}
if (mustZero)
Unsafe.InitBlock(_handle->Array + _handle->Length, 0, (uint)(i - _handle->Length));
_handle->Length = i;
}
if (count <= 8 && buffer != _handle->Array)
{
var byteCount = count;
while (--byteCount >= 0)
_handle->Array[_handle->Position + byteCount] = buffer[offset + byteCount];
}
else
{
Unsafe.CopyBlockUnaligned(_handle->Array + _handle->Position, buffer + offset, (uint)count);
}
_handle->Position = i;
}
/// <summary>
/// Write
/// </summary>
/// <param name="buffer">Buffer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(ReadOnlySpan<byte> buffer)
{
EnsureNotClosed();
var i = _handle->Position + buffer.Length;
if (i < 0)
throw new IOException("IO_StreamTooLong");
if (i > _handle->Length)
{
var mustZero = _handle->Position > _handle->Length;
if (i > _handle->Capacity)
{
var allocatedNewArray = EnsureCapacity(i);
if (allocatedNewArray)
mustZero = false;
}
if (mustZero)
Unsafe.InitBlock(_handle->Array + _handle->Length, 0, (uint)(i - _handle->Length));
_handle->Length = i;
}
Unsafe.CopyBlockUnaligned(ref *(_handle->Array + _handle->Position), ref MemoryMarshal.GetReference(buffer), (uint)buffer.Length);
_handle->Position = i;
}
/// <summary>
/// Write
/// </summary>
/// <param name="value">Byte</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteByte(byte value)
{
EnsureNotClosed();
if (_handle->Position >= _handle->Length)
{
var newLength = _handle->Position + 1;
var mustZero = _handle->Position > _handle->Length;
if (newLength >= _handle->Capacity)
{
var allocatedNewArray = EnsureCapacity(newLength);
if (allocatedNewArray)
mustZero = false;
}
if (mustZero)
Unsafe.InitBlock(_handle->Array + _handle->Length, 0, (uint)(_handle->Position - _handle->Length));
_handle->Length = newLength;
}
_handle->Array[_handle->Position++] = value;
}
/// <summary>
/// Ensure capacity
/// </summary>
/// <param name="capacity">Capacity</param>
/// <returns>Ensured</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool EnsureCapacity(int capacity)
{
if (capacity < 0)
throw new IOException("IO_StreamTooLong");
if (capacity > _handle->Capacity)
{
var newCapacity = capacity > 256 ? capacity : 256;
if (newCapacity < _handle->Capacity * 2)
newCapacity = _handle->Capacity * 2;
if ((uint)(_handle->Capacity * 2) > 2147483591)
newCapacity = capacity > 2147483591 ? capacity : 2147483591;
Capacity = newCapacity;
return true;
}
return false;
}
/// <summary>
/// Ensure not closed
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnsureNotClosed()
{
if (_handle == null)
throw new ObjectDisposedException("StreamClosed");
}
/// <summary>
/// Empty
/// </summary>
public static NativeMemoryStream Empty => new();
}
}

View File

@@ -0,0 +1,370 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native memory writer
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public unsafe ref struct NativeMemoryWriter
{
/// <summary>
/// Array
/// </summary>
public readonly byte* Array;
/// <summary>
/// Length
/// </summary>
public readonly int Length;
/// <summary>
/// Position
/// </summary>
public int Position;
/// <summary>
/// Structure
/// </summary>
/// <param name="array">Array</param>
/// <param name="length">Length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeMemoryWriter(byte* array, int length)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), length, "MustBeNonNegative");
Array = array;
Length = length;
Position = 0;
}
/// <summary>
/// Remaining
/// </summary>
public int Remaining => Length - Position;
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public byte* this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Array + index;
}
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public byte* this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Array + index;
}
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeMemoryWriter other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => throw new NotSupportedException("Cannot call Equals on NativeMemoryWriter");
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => HashCode.Combine((int)(nint)Array, Length, Position);
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => "NativeMemoryWriter";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeMemoryWriter left, NativeMemoryWriter right) => left.Array == right.Array && left.Length == right.Length && left.Position == right.Position;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeMemoryWriter left, NativeMemoryWriter right) => left.Array != right.Array || left.Length != right.Length || left.Position != right.Position;
/// <summary>
/// Advance
/// </summary>
/// <param name="count">Count</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Advance(int count)
{
var newPosition = Position + count;
if (newPosition < 0 || newPosition > Length)
throw new ArgumentOutOfRangeException(nameof(count), "Cannot advance past the end of the buffer.");
Position = newPosition;
}
/// <summary>
/// Try advance
/// </summary>
/// <param name="count">Count</param>
/// <returns>Advanced</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryAdvance(int count)
{
var newPosition = Position + count;
if (newPosition < 0 || newPosition > Length)
return false;
Position = newPosition;
return true;
}
/// <summary>
/// Write
/// </summary>
/// <param name="obj">object</param>
/// <typeparam name="T">Type</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write<T>(T* obj) where T : unmanaged
{
if (Position + sizeof(T) > Length)
throw new ArgumentOutOfRangeException(nameof(T), $"Requires size is {sizeof(T)}, but buffer length is {Remaining}.");
Unsafe.CopyBlockUnaligned(Array + Position, obj, (uint)sizeof(T));
Position += sizeof(T);
}
/// <summary>
/// Try write
/// </summary>
/// <param name="obj">object</param>
/// <typeparam name="T">Type</typeparam>
/// <returns>Wrote</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryWrite<T>(T* obj) where T : unmanaged
{
if (Position + sizeof(T) > Length)
return false;
Unsafe.CopyBlockUnaligned(Array + Position, obj, (uint)sizeof(T));
Position += sizeof(T);
return true;
}
/// <summary>
/// Write
/// </summary>
/// <param name="obj">object</param>
/// <param name="count">Count</param>
/// <typeparam name="T">Type</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write<T>(T* obj, int count) where T : unmanaged
{
count *= sizeof(T);
if (Position + count > Length)
throw new ArgumentOutOfRangeException(nameof(T), $"Requires size is {count}, but buffer length is {Remaining}.");
Unsafe.CopyBlockUnaligned(Array + Position, obj, (uint)count);
Position += count;
}
/// <summary>
/// Try write
/// </summary>
/// <param name="obj">object</param>
/// <param name="count">Count</param>
/// <typeparam name="T">Type</typeparam>
/// <returns>Wrote</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryWrite<T>(T* obj, int count) where T : unmanaged
{
count *= sizeof(T);
if (Position + count > Length)
return false;
Unsafe.CopyBlockUnaligned(Array + Position, obj, (uint)count);
Position += count;
return true;
}
/// <summary>
/// Write
/// </summary>
/// <param name="obj">object</param>
/// <typeparam name="T">Type</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write<T>(in T obj) where T : unmanaged
{
if (Position + sizeof(T) > Length)
throw new ArgumentOutOfRangeException(nameof(T), $"Requires size is {sizeof(T)}, but buffer length is {Remaining}.");
Unsafe.WriteUnaligned(Array + Position, obj);
Position += sizeof(T);
}
/// <summary>
/// Try write
/// </summary>
/// <param name="obj">object</param>
/// <typeparam name="T">Type</typeparam>
/// <returns>Wrote</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryWrite<T>(in T obj) where T : unmanaged
{
if (Position + sizeof(T) > Length)
return false;
Unsafe.WriteUnaligned(Array + Position, obj);
Position += sizeof(T);
return true;
}
/// <summary>
/// Write bytes
/// </summary>
/// <param name="buffer">Buffer</param>
/// <param name="length">Length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteBytes(byte* buffer, int length)
{
if (Position + length > Length)
throw new ArgumentOutOfRangeException(nameof(length), $"Requires size is {length}, but buffer length is {Remaining}.");
Unsafe.CopyBlockUnaligned(Array + Position, buffer, (uint)length);
Position += length;
}
/// <summary>
/// Try write bytes
/// </summary>
/// <param name="buffer">Buffer</param>
/// <param name="length">Length</param>
/// <returns>Wrote</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryWriteBytes(byte* buffer, int length)
{
if (Position + length > Length)
return false;
Unsafe.CopyBlockUnaligned(Array + Position, buffer, (uint)length);
Position += length;
return true;
}
/// <summary>
/// Clear
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() => Position = 0;
/// <summary>
/// As span
/// </summary>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<byte> AsSpan() => MemoryMarshal.CreateSpan(ref *Array, Position);
/// <summary>
/// As span
/// </summary>
/// <param name="length">Length</param>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<byte> AsSpan(int length) => MemoryMarshal.CreateSpan(ref *Array, length);
/// <summary>
/// As span
/// </summary>
/// <param name="start">Start</param>
/// <param name="length">Length</param>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<byte> AsSpan(int start, int length) => MemoryMarshal.CreateSpan(ref *(Array + start), length);
/// <summary>
/// As readOnly span
/// </summary>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<byte> AsReadOnlySpan() => MemoryMarshal.CreateReadOnlySpan(ref *Array, Position);
/// <summary>
/// As readOnly span
/// </summary>
/// <param name="length">Length</param>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<byte> AsReadOnlySpan(int length) => MemoryMarshal.CreateReadOnlySpan(ref *Array, length);
/// <summary>
/// As readOnly span
/// </summary>
/// <param name="start">Start</param>
/// <param name="length">Length</param>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<byte> AsReadOnlySpan(int start, int length) => MemoryMarshal.CreateReadOnlySpan(ref *(Array + start), length);
/// <summary>
/// As span
/// </summary>
/// <returns>Span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Span<byte>(NativeMemoryWriter writer) => writer.AsSpan();
/// <summary>
/// As readOnly span
/// </summary>
/// <returns>ReadOnlySpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<byte>(NativeMemoryWriter writer) => writer.AsReadOnlySpan();
/// <summary>
/// As native memory reader
/// </summary>
/// <returns>NativeMemoryReader</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NativeMemoryReader(NativeMemoryWriter writer) => new(writer.Array, writer.Position);
/// <summary>
/// As native memory writer
/// </summary>
/// <returns>NativeMemoryWriter</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NativeMemoryWriter(NativeArray<byte> writer) => new(writer.Array, writer.Length);
/// <summary>
/// As native memory writer
/// </summary>
/// <returns>NativeMemoryWriter</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NativeMemoryWriter(NativeMemoryArray<byte> writer) => new(writer.Array, writer.Length);
/// <summary>
/// As native memory writer
/// </summary>
/// <returns>NativeMemoryWriter</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NativeMemoryWriter(NativeArraySegment<byte> writer) => new(writer.Array + writer.Offset, writer.Count);
/// <summary>
/// Empty
/// </summary>
public static NativeMemoryWriter Empty => new();
}
}

View File

@@ -0,0 +1,210 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if NET7_0_OR_GREATER
#endif
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
using System.Threading;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8604
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native monitorLock
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct NativeMonitorLock : IDisposable, IEquatable<NativeMonitorLock>
{
/// <summary>
/// Handle
/// </summary>
private GCHandle _handle;
/// <summary>
/// Structure
/// </summary>
/// <param name="value">Value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeMonitorLock(object value) => _handle = GCHandle.Alloc(value, GCHandleType.Normal);
/// <summary>
/// Structure
/// </summary>
/// <param name="value">Value</param>
/// <param name="type">GCHandle type</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeMonitorLock(object value, GCHandleType type) => _handle = GCHandle.Alloc(value, type);
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle.IsAllocated;
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeMonitorLock other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeMonitorLock nativeMonitorLock && nativeMonitorLock == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => "NativeMonitorLock";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeMonitorLock left, NativeMonitorLock right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeMonitorLock left, NativeMonitorLock right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (!_handle.IsAllocated)
return;
_handle.Free();
}
/// <summary>
/// Enter
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Enter() => Monitor.Enter(_handle.Target);
/// <summary>
/// Enter
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Enter(ref bool lockTaken) => Monitor.Enter(_handle.Target, ref lockTaken);
/// <summary>
/// Enter
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryEnter() => Monitor.TryEnter(_handle.Target);
/// <summary>
/// Enter
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void TryEnter(ref bool lockTaken) => Monitor.TryEnter(_handle.Target, ref lockTaken);
/// <summary>
/// Enter
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryEnter(int millisecondsTimeout) => Monitor.TryEnter(_handle.Target, millisecondsTimeout);
/// <summary>
/// Enter
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void TryEnter(int millisecondsTimeout, ref bool lockTaken) => Monitor.TryEnter(_handle.Target, millisecondsTimeout, ref lockTaken);
/// <summary>
/// Is entered
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsEntered() => Monitor.IsEntered(_handle.Target);
/// <summary>
/// Wait
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Wait(int millisecondsTimeout) => Monitor.Wait(_handle.Target, millisecondsTimeout);
/// <summary>
/// Pulse
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Pulse() => Monitor.Pulse(_handle.Target);
/// <summary>
/// Pulse all
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PulseAll() => Monitor.PulseAll(_handle.Target);
/// <summary>
/// Try enter
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryEnter(TimeSpan timeout) => Monitor.TryEnter(_handle.Target, timeout);
/// <summary>
/// Try enter
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void TryEnter(TimeSpan timeout, ref bool lockTaken) => Monitor.TryEnter(_handle.Target, timeout, ref lockTaken);
/// <summary>
/// Wait
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Wait(TimeSpan timeout) => Monitor.Wait(_handle.Target, timeout);
/// <summary>
/// Wait
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Wait() => Monitor.Wait(_handle.Target);
/// <summary>
/// Wait
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Wait(int millisecondsTimeout, bool exitContext) => Monitor.Wait(_handle.Target, millisecondsTimeout, exitContext);
/// <summary>
/// Wait
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Wait(TimeSpan timeout, bool exitContext) => Monitor.Wait(_handle.Target, timeout, exitContext);
/// <summary>
/// Exit
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Exit() => Monitor.Exit(_handle.Target);
/// <summary>
/// Empty
/// </summary>
public static NativeMonitorLock Empty => new();
}
}

View File

@@ -0,0 +1,618 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native priorityQueue
/// </summary>
/// <typeparam name="TElement">Type</typeparam>
/// <typeparam name="TPriority">Type</typeparam>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativePriorityQueue<TElement, TPriority> : IDisposable, IEquatable<NativePriorityQueue<TElement, TPriority>> where TElement : unmanaged where TPriority : unmanaged, IComparable<TPriority>
{
/// <summary>
/// Handle
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct NativePriorityQueueHandle
{
/// <summary>
/// Nodes
/// </summary>
public ValueTuple<TElement, TPriority>* Nodes;
/// <summary>
/// Length
/// </summary>
public int Length;
/// <summary>
/// Unordered items
/// </summary>
public UnorderedItemsCollection UnorderedItems;
/// <summary>
/// Size
/// </summary>
public int Size;
/// <summary>
/// Version
/// </summary>
public int Version;
}
/// <summary>
/// Handle
/// </summary>
private readonly NativePriorityQueueHandle* _handle;
/// <summary>
/// Structure
/// </summary>
/// <param name="capacity">Capacity</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativePriorityQueue(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "MustBeNonNegative");
if (capacity < 4)
capacity = 4;
_handle = (NativePriorityQueueHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativePriorityQueueHandle));
_handle->Nodes = (ValueTuple<TElement, TPriority>*)NativeMemoryAllocator.Alloc((uint)(capacity * sizeof(ValueTuple<TElement, TPriority>)));
_handle->Length = capacity;
_handle->UnorderedItems = new UnorderedItemsCollection(this);
_handle->Size = 0;
_handle->Version = 0;
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle != null;
/// <summary>
/// Is empty
/// </summary>
public bool IsEmpty => _handle->Size == 0;
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public (TElement Element, TPriority Priority) this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _handle->Nodes[index];
}
/// <summary>
/// Count
/// </summary>
public int Count => _handle->Size;
/// <summary>
/// Unordered items
/// </summary>
public UnorderedItemsCollection UnorderedItems => _handle->UnorderedItems;
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativePriorityQueue<TElement, TPriority> other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativePriorityQueue<TElement, TPriority> nativeQueue && nativeQueue == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"NativePriorityQueue<{typeof(TElement).Name}, {typeof(TPriority).Name}>";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativePriorityQueue<TElement, TPriority> left, NativePriorityQueue<TElement, TPriority> right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativePriorityQueue<TElement, TPriority> left, NativePriorityQueue<TElement, TPriority> right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_handle == null)
return;
NativeMemoryAllocator.Free(_handle->Nodes);
NativeMemoryAllocator.Free(_handle);
}
/// <summary>
/// Clear
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
_handle->Size = 0;
++_handle->Version;
}
/// <summary>
/// Enqueue
/// </summary>
/// <param name="element">Element</param>
/// <param name="priority">Priority</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Enqueue(in TElement element, in TPriority priority)
{
var size = _handle->Size;
++_handle->Version;
if (_handle->Length == size)
Grow(size + 1);
_handle->Size = size + 1;
MoveUp(new ValueTuple<TElement, TPriority>(element, priority), size);
}
/// <summary>
/// Try enqueue
/// </summary>
/// <param name="element">Element</param>
/// <param name="priority">Priority</param>
/// <returns>Enqueued</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryEnqueue(in TElement element, in TPriority priority)
{
var size = _handle->Size;
++_handle->Version;
if (_handle->Length != size)
{
_handle->Size = size + 1;
MoveUp(new ValueTuple<TElement, TPriority>(element, priority), size);
return true;
}
return false;
}
/// <summary>
/// Enqueue dequeue
/// </summary>
/// <param name="element">Element</param>
/// <param name="priority">Priority</param>
/// <returns>Element</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TElement EnqueueDequeue(in TElement element, in TPriority priority)
{
if (_handle->Size != 0)
{
var node = _handle->Nodes[0];
if (priority.CompareTo(node.Item2) > 0)
{
MoveDown(new ValueTuple<TElement, TPriority>(element, priority), 0);
++_handle->Version;
return node.Item1;
}
}
return element;
}
/// <summary>
/// Try enqueue dequeue
/// </summary>
/// <param name="element">Element</param>
/// <param name="priority">Priority</param>
/// <param name="result">Element</param>
/// <returns>Enqueued</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryEnqueueDequeue(in TElement element, in TPriority priority, out TElement result)
{
if (_handle->Size != 0)
{
var node = _handle->Nodes[0];
if (priority.CompareTo(node.Item2) > 0)
{
MoveDown(new ValueTuple<TElement, TPriority>(element, priority), 0);
++_handle->Version;
result = node.Item1;
return true;
}
}
result = element;
return false;
}
/// <summary>
/// Dequeue
/// </summary>
/// <returns>Item</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TElement Dequeue()
{
if (_handle->Size == 0)
throw new InvalidOperationException("EmptyQueue");
var element = _handle->Nodes[0].Item1;
RemoveRootNode();
return element;
}
/// <summary>
/// Try dequeue
/// </summary>
/// <param name="element">Element</param>
/// <returns>Dequeued</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryDequeue(out TElement element)
{
if (_handle->Size != 0)
{
var tuple = _handle->Nodes[0];
element = tuple.Item1;
RemoveRootNode();
return true;
}
element = default;
return false;
}
/// <summary>
/// Try dequeue
/// </summary>
/// <param name="element">Element</param>
/// <param name="priority">Priority</param>
/// <returns>Dequeued</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryDequeue(out TElement element, out TPriority priority)
{
if (_handle->Size != 0)
{
var tuple = _handle->Nodes[0];
element = tuple.Item1;
priority = tuple.Item2;
RemoveRootNode();
return true;
}
element = default;
priority = default;
return false;
}
/// <summary>
/// Dequeue enqueue
/// </summary>
/// <param name="element">Element</param>
/// <param name="priority">Priority</param>
/// <returns>Element</returns>
public TElement DequeueEnqueue(in TElement element, in TPriority priority)
{
if (_handle->Size == 0)
throw new InvalidOperationException("EmptyQueue");
var node = _handle->Nodes[0];
if (priority.CompareTo(node.Item2) > 0)
MoveDown(new ValueTuple<TElement, TPriority>(element, priority), 0);
else
_handle->Nodes[0] = new ValueTuple<TElement, TPriority>(element, priority);
++_handle->Version;
return node.Item1;
}
/// <summary>
/// Try dequeue enqueue
/// </summary>
/// <param name="element">Element</param>
/// <param name="priority">Priority</param>
/// <param name="result">Element</param>
/// <returns>Dequeued</returns>
public bool TryDequeueEnqueue(in TElement element, in TPriority priority, out TElement result)
{
if (_handle->Size == 0)
{
result = default;
return false;
}
var node = _handle->Nodes[0];
if (priority.CompareTo(node.Item2) > 0)
MoveDown(new ValueTuple<TElement, TPriority>(element, priority), 0);
else
_handle->Nodes[0] = new ValueTuple<TElement, TPriority>(element, priority);
++_handle->Version;
result = node.Item1;
return true;
}
/// <summary>
/// Peek
/// </summary>
/// <returns>Item</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TElement Peek() => _handle->Size == 0 ? throw new InvalidOperationException("EmptyQueue") : _handle->Nodes[0].Item1;
/// <summary>
/// Try peek
/// </summary>
/// <param name="element">Element</param>
/// <param name="priority">Priority</param>
/// <returns>Peeked</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPeek(out TElement element, out TPriority priority)
{
if (_handle->Size != 0)
{
var tuple = _handle->Nodes[0];
element = tuple.Item1;
priority = tuple.Item2;
return true;
}
element = default;
priority = default;
return false;
}
/// <summary>
/// Ensure capacity
/// </summary>
/// <param name="capacity">Capacity</param>
/// <returns>New capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int EnsureCapacity(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "MustBeNonNegative");
if (_handle->Length < capacity)
{
Grow(capacity);
++_handle->Version;
}
return _handle->Length;
}
/// <summary>
/// Trim excess
/// </summary>
/// <returns>New capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void TrimExcess()
{
if (_handle->Size >= (int)(_handle->Length * 0.9))
return;
var nodes = (ValueTuple<TElement, TPriority>*)NativeMemoryAllocator.Alloc((uint)(_handle->Size * sizeof(ValueTuple<TElement, TPriority>)));
Unsafe.CopyBlockUnaligned(nodes, _handle->Nodes, (uint)_handle->Size);
NativeMemoryAllocator.Free(_handle->Nodes);
_handle->Nodes = nodes;
_handle->Length = _handle->Size;
++_handle->Version;
}
/// <summary>
/// Grow
/// </summary>
/// <param name="capacity">Capacity</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Grow(int capacity)
{
var newCapacity = 2 * _handle->Length;
if ((uint)newCapacity > 2147483591)
newCapacity = 2147483591;
var expected = _handle->Length + 4;
newCapacity = newCapacity > expected ? newCapacity : expected;
if (newCapacity < capacity)
newCapacity = capacity;
var nodes = (ValueTuple<TElement, TPriority>*)NativeMemoryAllocator.Alloc((uint)(newCapacity * sizeof(ValueTuple<TElement, TPriority>)));
Unsafe.CopyBlockUnaligned(nodes, _handle->Nodes, (uint)_handle->Size);
NativeMemoryAllocator.Free(_handle->Nodes);
_handle->Nodes = nodes;
_handle->Length = newCapacity;
}
/// <summary>
/// Remove root node
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void RemoveRootNode()
{
var index = --_handle->Size;
++_handle->Version;
if (index > 0)
{
var node = _handle->Nodes[index];
MoveDown(node, 0);
}
}
/// <summary>
/// Move up
/// </summary>
/// <param name="node">Node</param>
/// <param name="nodeIndex">Node index</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void MoveUp(in ValueTuple<TElement, TPriority> node, int nodeIndex)
{
var nodes = _handle->Nodes;
int parentIndex;
for (; nodeIndex > 0; nodeIndex = parentIndex)
{
parentIndex = (nodeIndex - 1) >> 2;
var tuple = nodes[parentIndex];
if (node.Item2.CompareTo(tuple.Item2) < 0)
nodes[nodeIndex] = tuple;
else
break;
}
nodes[nodeIndex] = node;
}
/// <summary>
/// Move down
/// </summary>
/// <param name="node">Node</param>
/// <param name="nodeIndex">Node index</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void MoveDown(in ValueTuple<TElement, TPriority> node, int nodeIndex)
{
var nodes = _handle->Nodes;
int firstChildIndex;
int first;
for (var size = _handle->Size; (firstChildIndex = (nodeIndex << 2) + 1) < size; nodeIndex = first)
{
var valueTuple = nodes[firstChildIndex];
first = firstChildIndex;
var minSize = firstChildIndex + 4;
var second = minSize <= size ? minSize : size;
while (++firstChildIndex < second)
{
var tuple = nodes[firstChildIndex];
if (tuple.Item2.CompareTo(valueTuple.Item2) < 0)
{
valueTuple = tuple;
first = firstChildIndex;
}
}
if (node.Item2.CompareTo(valueTuple.Item2) > 0)
nodes[nodeIndex] = valueTuple;
else
break;
}
nodes[nodeIndex] = node;
}
/// <summary>
/// Empty
/// </summary>
public static NativePriorityQueue<TElement, TPriority> Empty => new();
/// <summary>
/// Unordered items collection
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public readonly struct UnorderedItemsCollection
{
/// <summary>
/// NativePriorityQueue
/// </summary>
private readonly NativePriorityQueue<TElement, TPriority> _nativePriorityQueue;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativePriorityQueue">Native priorityQueue</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal UnorderedItemsCollection(NativePriorityQueue<TElement, TPriority> nativePriorityQueue) => _nativePriorityQueue = nativePriorityQueue;
/// <summary>
/// Get enumerator
/// </summary>
/// <returns>Enumerator</returns>
public Enumerator GetEnumerator() => new(_nativePriorityQueue);
/// <summary>
/// Enumerator
/// </summary>
public struct Enumerator
{
/// <summary>
/// NativePriorityQueue
/// </summary>
private readonly NativePriorityQueue<TElement, TPriority> _nativePriorityQueue;
/// <summary>
/// Version
/// </summary>
private readonly int _version;
/// <summary>
/// Index
/// </summary>
private int _index;
/// <summary>
/// Current
/// </summary>
private ValueTuple<TElement, TPriority> _current;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativePriorityQueue">Native priorityQueue</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(NativePriorityQueue<TElement, TPriority> nativePriorityQueue)
{
_nativePriorityQueue = nativePriorityQueue;
_index = 0;
_version = nativePriorityQueue._handle->Version;
_current = default;
}
/// <summary>
/// Move next
/// </summary>
/// <returns>Moved</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
if (_version != _nativePriorityQueue._handle->Version)
throw new InvalidOperationException("EnumFailedVersion");
if ((uint)_index >= (uint)_nativePriorityQueue._handle->Size)
{
_index = _nativePriorityQueue._handle->Size + 1;
_current = default;
return false;
}
_current = _nativePriorityQueue._handle->Nodes[_index];
++_index;
return true;
}
/// <summary>
/// Current
/// </summary>
public (TElement Element, TPriority Priority) Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _current;
}
}
}
}
}

View File

@@ -0,0 +1,455 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native queue
/// </summary>
/// <typeparam name="T">Type</typeparam>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeQueue<T> : IDisposable, IEquatable<NativeQueue<T>> where T : unmanaged
{
/// <summary>
/// Handle
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct NativeQueueHandle
{
/// <summary>
/// Array
/// </summary>
public T* Array;
/// <summary>
/// Length
/// </summary>
public int Length;
/// <summary>
/// Head
/// </summary>
public int Head;
/// <summary>
/// Tail
/// </summary>
public int Tail;
/// <summary>
/// Size
/// </summary>
public int Size;
/// <summary>
/// Version
/// </summary>
public int Version;
}
/// <summary>
/// Handle
/// </summary>
private readonly NativeQueueHandle* _handle;
/// <summary>
/// Structure
/// </summary>
/// <param name="capacity">Capacity</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeQueue(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "MustBeNonNegative");
if (capacity < 4)
capacity = 4;
_handle = (NativeQueueHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeQueueHandle));
_handle->Array = (T*)NativeMemoryAllocator.Alloc((uint)(capacity * sizeof(T)));
_handle->Length = capacity;
_handle->Head = 0;
_handle->Tail = 0;
_handle->Size = 0;
_handle->Version = 0;
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle != null;
/// <summary>
/// Is empty
/// </summary>
public bool IsEmpty => _handle->Size == 0;
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public ref T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _handle->Array[index];
}
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public ref T this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _handle->Array[index];
}
/// <summary>
/// Count
/// </summary>
public int Count => _handle->Size;
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeQueue<T> other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeQueue<T> nativeQueue && nativeQueue == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"NativeQueue<{typeof(T).Name}>";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeQueue<T> left, NativeQueue<T> right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeQueue<T> left, NativeQueue<T> right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_handle == null)
return;
NativeMemoryAllocator.Free(_handle->Array);
NativeMemoryAllocator.Free(_handle);
}
/// <summary>
/// Clear
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
_handle->Size = 0;
_handle->Head = 0;
_handle->Tail = 0;
_handle->Version++;
}
/// <summary>
/// Enqueue
/// </summary>
/// <param name="item">Item</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Enqueue(in T item)
{
if (_handle->Size == _handle->Length)
Grow(_handle->Size + 1);
_handle->Array[_handle->Tail] = item;
MoveNext(ref _handle->Tail);
_handle->Size++;
_handle->Version++;
}
/// <summary>
/// Try enqueue
/// </summary>
/// <param name="item">Item</param>
/// <returns>Enqueued</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryEnqueue(in T item)
{
if (_handle->Size != _handle->Length)
{
_handle->Array[_handle->Tail] = item;
MoveNext(ref _handle->Tail);
_handle->Size++;
_handle->Version++;
return true;
}
return false;
}
/// <summary>
/// Dequeue
/// </summary>
/// <returns>Item</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Dequeue()
{
if (_handle->Size == 0)
throw new InvalidOperationException("EmptyQueue");
var removed = _handle->Array[_handle->Head];
MoveNext(ref _handle->Head);
_handle->Size--;
_handle->Version++;
return removed;
}
/// <summary>
/// Try dequeue
/// </summary>
/// <param name="result">Item</param>
/// <returns>Dequeued</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryDequeue(out T result)
{
if (_handle->Size == 0)
{
result = default;
return false;
}
result = _handle->Array[_handle->Head];
MoveNext(ref _handle->Head);
_handle->Size--;
_handle->Version++;
return true;
}
/// <summary>
/// Peek
/// </summary>
/// <returns>Item</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Peek() => _handle->Size == 0 ? throw new InvalidOperationException("EmptyQueue") : _handle->Array[_handle->Head];
/// <summary>
/// Try peek
/// </summary>
/// <param name="result">Item</param>
/// <returns>Peeked</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPeek(out T result)
{
if (_handle->Size == 0)
{
result = default;
return false;
}
result = _handle->Array[_handle->Head];
return true;
}
/// <summary>
/// Ensure capacity
/// </summary>
/// <param name="capacity">Capacity</param>
/// <returns>New capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int EnsureCapacity(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "MustBeNonNegative");
if (_handle->Length < capacity)
Grow(capacity);
return _handle->Length;
}
/// <summary>
/// Trim excess
/// </summary>
/// <returns>New capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int TrimExcess()
{
var threshold = (int)(_handle->Length * 0.9);
if (_handle->Size < threshold)
SetCapacity(_handle->Size);
return _handle->Length;
}
/// <summary>
/// Set capacity
/// </summary>
/// <param name="capacity">Capacity</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetCapacity(int capacity)
{
var newArray = (T*)NativeMemoryAllocator.Alloc((uint)(capacity * sizeof(T)));
if (_handle->Size > 0)
{
if (_handle->Head < _handle->Tail)
{
Unsafe.CopyBlockUnaligned(newArray, _handle->Array + _handle->Head, (uint)(_handle->Size * sizeof(T)));
}
else
{
Unsafe.CopyBlockUnaligned(newArray, _handle->Array + _handle->Head, (uint)((_handle->Length - _handle->Head) * sizeof(T)));
Unsafe.CopyBlockUnaligned(newArray + _handle->Length - _handle->Head, _handle->Array, (uint)(_handle->Tail * sizeof(T)));
}
}
NativeMemoryAllocator.Free(_handle->Array);
_handle->Array = newArray;
_handle->Length = capacity;
_handle->Head = 0;
_handle->Tail = _handle->Size == capacity ? 0 : _handle->Size;
_handle->Version++;
}
/// <summary>
/// Grow
/// </summary>
/// <param name="capacity">Capacity</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Grow(int capacity)
{
var newCapacity = 2 * _handle->Length;
if ((uint)newCapacity > 2147483591)
newCapacity = 2147483591;
var expected = _handle->Length + 4;
newCapacity = newCapacity > expected ? newCapacity : expected;
if (newCapacity < capacity)
newCapacity = capacity;
SetCapacity(newCapacity);
}
/// <summary>
/// Move next
/// </summary>
/// <param name="index">Index</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void MoveNext(ref int index)
{
var tmp = index + 1;
if (tmp == _handle->Length)
tmp = 0;
index = tmp;
}
/// <summary>
/// Empty
/// </summary>
public static NativeQueue<T> Empty => new();
/// <summary>
/// Get enumerator
/// </summary>
/// <returns>Enumerator</returns>
public Enumerator GetEnumerator() => new(this);
/// <summary>
/// Enumerator
/// </summary>
public struct Enumerator
{
/// <summary>
/// NativeQueue
/// </summary>
private readonly NativeQueue<T> _nativeQueue;
/// <summary>
/// Version
/// </summary>
private readonly int _version;
/// <summary>
/// Index
/// </summary>
private int _index;
/// <summary>
/// Current
/// </summary>
private T _currentElement;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeQueue">NativeQueue</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(in NativeQueue<T> nativeQueue)
{
_nativeQueue = nativeQueue;
_version = nativeQueue._handle->Version;
_index = -1;
_currentElement = default;
}
/// <summary>
/// Move next
/// </summary>
/// <returns>Moved</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
if (_version != _nativeQueue._handle->Version)
throw new InvalidOperationException("EnumFailedVersion");
if (_index == -2)
return false;
_index++;
if (_index == _nativeQueue._handle->Size)
{
_index = -2;
_currentElement = default;
return false;
}
var array = _nativeQueue._handle->Array;
var capacity = (uint)_nativeQueue._handle->Length;
var arrayIndex = (uint)(_nativeQueue._handle->Head + _index);
if (arrayIndex >= capacity)
arrayIndex -= capacity;
_currentElement = array[arrayIndex];
return true;
}
/// <summary>
/// Current
/// </summary>
public T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _index < 0 ? throw new InvalidOperationException(_index == -1 ? "EnumNotStarted" : "EnumEnded") : _currentElement;
}
}
}
}

View File

@@ -0,0 +1,149 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native reference
/// </summary>
/// <typeparam name="T">Type</typeparam>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeReference<T> : IDisposable, IEquatable<NativeReference<T>> where T : unmanaged
{
/// <summary>
/// Handle
/// </summary>
private readonly T* _handle;
/// <summary>
/// Structure
/// </summary>
/// <param name="handle">Handle</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeReference(T* handle) => _handle = handle;
/// <summary>
/// Structure
/// </summary>
/// <param name="handle">Handle</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeReference(nint handle) => _handle = (T*)handle;
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle != null;
/// <summary>
/// Handle
/// </summary>
public T* Handle
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _handle;
}
/// <summary>
/// Value
/// </summary>
public ref T Value
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref *_handle;
}
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeReference<T> other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeReference<T> nativeReference && nativeReference == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"NativeReference<{typeof(T).Name}>";
/// <summary>
/// As reference
/// </summary>
/// <returns>NativeReference</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NativeReference<T>(T* handle) => new(handle);
/// <summary>
/// As handle
/// </summary>
/// <returns>Handle</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator T*(NativeReference<T> nativeReference) => nativeReference._handle;
/// <summary>
/// As reference
/// </summary>
/// <returns>NativeReference</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NativeReference<T>(nint handle) => new((T*)handle);
/// <summary>
/// As handle
/// </summary>
/// <returns>Handle</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator nint(NativeReference<T> nativeReference) => (nint)nativeReference._handle;
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeReference<T> left, NativeReference<T> right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeReference<T> left, NativeReference<T> right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_handle == null)
return;
NativeMemoryAllocator.Free(_handle);
}
/// <summary>
/// Empty
/// </summary>
public static NativeReference<T> Empty => new();
}
}

View File

@@ -0,0 +1,695 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
using System.Collections.Generic;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native sortedList
/// </summary>
/// <typeparam name="TKey">Type</typeparam>
/// <typeparam name="TValue">Type</typeparam>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeSortedList<TKey, TValue> where TKey : unmanaged, IComparable<TKey> where TValue : unmanaged
{
/// <summary>
/// Handle
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct NativeSortedListHandle
{
/// <summary>
/// Keys
/// </summary>
public TKey* Buckets;
/// <summary>
/// Values
/// </summary>
public TValue* Entries;
/// <summary>
/// Size
/// </summary>
public int Size;
/// <summary>
/// Version
/// </summary>
public int Version;
/// <summary>
/// Capacity
/// </summary>
public int Capacity;
/// <summary>
/// Keys
/// </summary>
public KeyCollection Keys;
/// <summary>
/// Values
/// </summary>
public ValueCollection Values;
}
/// <summary>
/// Handle
/// </summary>
private readonly NativeSortedListHandle* _handle;
/// <summary>
/// Keys
/// </summary>
public KeyCollection Keys => _handle->Keys;
/// <summary>
/// Values
/// </summary>
public ValueCollection Values => _handle->Values;
/// <summary>
/// Structure
/// </summary>
/// <param name="capacity">Capacity</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeSortedList(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "MustBeNonNegative");
if (capacity < 4)
capacity = 4;
_handle = (NativeSortedListHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeSortedListHandle));
_handle->Buckets = (TKey*)NativeMemoryAllocator.Alloc((uint)(capacity * sizeof(TKey)));
_handle->Entries = (TValue*)NativeMemoryAllocator.Alloc((uint)(capacity * sizeof(TValue)));
_handle->Size = 0;
_handle->Version = 0;
_handle->Capacity = capacity;
_handle->Keys = new KeyCollection(this);
_handle->Values = new ValueCollection(this);
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle != null;
/// <summary>
/// Is empty
/// </summary>
public bool IsEmpty => _handle->Size == 0;
/// <summary>
/// Get or set value
/// </summary>
/// <param name="key">Key</param>
public TValue this[TKey key]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
var index = BinarySearch(_handle->Buckets, _handle->Size, key);
return index >= 0 ? _handle->Entries[index] : throw new KeyNotFoundException(key.ToString());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
var index = BinarySearch(_handle->Buckets, _handle->Size, key);
if (index >= 0)
{
_handle->Entries[index] = value;
++_handle->Version;
}
else
{
Insert(~index, key, value);
}
}
}
/// <summary>
/// Count
/// </summary>
public int Count => _handle->Size;
/// <summary>
/// Capacity
/// </summary>
public int Capacity
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _handle->Capacity;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
if (value < _handle->Size)
throw new ArgumentOutOfRangeException(nameof(value), value, "SmallCapacity");
if (value != _handle->Capacity)
{
if (value > 0)
{
var keys = (TKey*)NativeMemoryAllocator.Alloc((uint)(value * sizeof(TKey)));
var values = (TValue*)NativeMemoryAllocator.Alloc((uint)(value * sizeof(TValue)));
if (_handle->Size > 0)
{
Unsafe.CopyBlockUnaligned(keys, _handle->Buckets, (uint)(_handle->Size * sizeof(TKey)));
Unsafe.CopyBlockUnaligned(values, _handle->Entries, (uint)(_handle->Size * sizeof(TValue)));
}
NativeMemoryAllocator.Free(_handle->Buckets);
NativeMemoryAllocator.Free(_handle->Entries);
_handle->Buckets = keys;
_handle->Entries = values;
}
else
{
NativeMemoryAllocator.Free(_handle->Buckets);
NativeMemoryAllocator.Free(_handle->Entries);
_handle->Buckets = (TKey*)NativeMemoryAllocator.Alloc(0);
_handle->Entries = (TValue*)NativeMemoryAllocator.Alloc(0);
}
_handle->Capacity = value;
}
}
}
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeSortedList<TKey, TValue> other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeSortedList<TKey, TValue> nativeSortedList && nativeSortedList == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"NativeSortedList<{typeof(TKey).Name}, {typeof(TValue).Name}>";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeSortedList<TKey, TValue> left, NativeSortedList<TKey, TValue> right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeSortedList<TKey, TValue> left, NativeSortedList<TKey, TValue> right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_handle == null)
return;
NativeMemoryAllocator.Free(_handle->Buckets);
NativeMemoryAllocator.Free(_handle->Entries);
NativeMemoryAllocator.Free(_handle);
}
/// <summary>
/// Clear
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
++_handle->Version;
_handle->Size = 0;
}
/// <summary>
/// Add
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(in TKey key, in TValue value)
{
var num = BinarySearch(_handle->Buckets, _handle->Size, key);
if (num >= 0)
throw new ArgumentException($"AddingDuplicate, {key}", nameof(key));
Insert(~num, key, value);
}
/// <summary>
/// Remove
/// </summary>
/// <param name="key">Key</param>
/// <returns>Removed</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(in TKey key)
{
var index = BinarySearch(_handle->Buckets, _handle->Size, key);
if (index >= 0)
{
--_handle->Size;
if (index < _handle->Size)
{
Unsafe.CopyBlockUnaligned(_handle->Buckets + index, _handle->Buckets + index + 1, (uint)((_handle->Size - index) * sizeof(TKey)));
Unsafe.CopyBlockUnaligned(_handle->Entries + index, _handle->Entries + index + 1, (uint)((_handle->Size - index) * sizeof(TValue)));
}
++_handle->Version;
return true;
}
return false;
}
/// <summary>
/// Remove
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
/// <returns>Removed</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(in TKey key, out TValue value)
{
var index = BinarySearch(_handle->Buckets, _handle->Size, key);
if (index >= 0)
{
value = _handle->Entries[index];
--_handle->Size;
if (index < _handle->Size)
{
Unsafe.CopyBlockUnaligned(_handle->Buckets + index, _handle->Buckets + index + 1, (uint)((_handle->Size - index) * sizeof(TKey)));
Unsafe.CopyBlockUnaligned(_handle->Entries + index, _handle->Entries + index + 1, (uint)((_handle->Size - index) * sizeof(TValue)));
}
++_handle->Version;
return true;
}
value = default;
return false;
}
/// <summary>
/// Contains key
/// </summary>
/// <param name="key">Key</param>
/// <returns>Contains key</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ContainsKey(in TKey key) => BinarySearch(_handle->Buckets, _handle->Size, key) >= 0;
/// <summary>
/// Try to get the value
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
/// <returns>Got</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(in TKey key, out TValue value)
{
var index = BinarySearch(_handle->Buckets, _handle->Size, key);
if (index >= 0)
{
value = _handle->Entries[index];
return true;
}
value = default;
return false;
}
/// <summary>
/// Ensure capacity
/// </summary>
/// <param name="capacity">Capacity</param>
/// <returns>New capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnsureCapacity(int capacity)
{
if (_handle->Capacity < capacity)
{
var newCapacity = 2 * _handle->Capacity;
if ((uint)newCapacity > 2147483591)
newCapacity = 2147483591;
var expected = _handle->Capacity + 4;
newCapacity = newCapacity > expected ? newCapacity : expected;
if (newCapacity < capacity)
newCapacity = capacity;
Capacity = newCapacity;
}
}
/// <summary>
/// Trim excess
/// </summary>
/// <returns>New capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int TrimExcess()
{
var threshold = (int)(_handle->Capacity * 0.9);
if (_handle->Size < threshold)
Capacity = _handle->Size;
return _handle->Capacity;
}
/// <summary>
/// Insert
/// </summary>
/// <param name="index">Index</param>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Insert(int index, in TKey key, in TValue value)
{
if (_handle->Size == _handle->Capacity)
EnsureCapacity(_handle->Size + 1);
if (index < _handle->Size)
{
Unsafe.CopyBlockUnaligned(_handle->Buckets + index + 1, _handle->Buckets + index, (uint)((_handle->Size - index) * sizeof(TKey)));
Unsafe.CopyBlockUnaligned(_handle->Entries + index + 1, _handle->Entries + index, (uint)((_handle->Size - index) * sizeof(TValue)));
}
_handle->Buckets[index] = key;
_handle->Entries[index] = value;
++_handle->Size;
++_handle->Version;
}
/// <summary>
/// Binary search
/// </summary>
/// <param name="start">Start</param>
/// <param name="length">Length</param>
/// <param name="comparable">Comparable</param>
/// <returns>Index</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int BinarySearch(TKey* start, int length, in TKey comparable)
{
var low = 0;
var high = length - 1;
while (low <= high)
{
var i = (int)(((uint)high + (uint)low) >> 1);
var c = comparable.CompareTo(*(start + i));
if (c == 0)
return i;
if (c > 0)
low = i + 1;
else
high = i - 1;
}
return ~low;
}
/// <summary>
/// Empty
/// </summary>
public static NativeSortedList<TKey, TValue> Empty => new();
/// <summary>
/// Get enumerator
/// </summary>
/// <returns>Enumerator</returns>
public Enumerator GetEnumerator() => new(this);
/// <summary>
/// Enumerator
/// </summary>
public struct Enumerator
{
/// <summary>
/// NativeSortedList
/// </summary>
private readonly NativeSortedList<TKey, TValue> _nativeSortedList;
/// <summary>
/// Current
/// </summary>
private KeyValuePair<TKey, TValue> _current;
/// <summary>
/// Index
/// </summary>
private int _index;
/// <summary>
/// Version
/// </summary>
private readonly int _version;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeSortedList">NativeSortedList</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(NativeSortedList<TKey, TValue> nativeSortedList)
{
_nativeSortedList = nativeSortedList;
_current = default;
_index = 0;
_version = _nativeSortedList._handle->Version;
}
/// <summary>
/// Move next
/// </summary>
/// <returns>Moved</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
if (_version != _nativeSortedList._handle->Version)
throw new InvalidOperationException("EnumFailedVersion");
if ((uint)_index < (uint)_nativeSortedList._handle->Size)
{
_current = new KeyValuePair<TKey, TValue>(_nativeSortedList._handle->Buckets[_index], _nativeSortedList._handle->Entries[_index]);
++_index;
return true;
}
_index = _nativeSortedList._handle->Size + 1;
return false;
}
/// <summary>
/// Current
/// </summary>
public KeyValuePair<TKey, TValue> Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _current;
}
}
/// <summary>
/// Key collection
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public readonly struct KeyCollection
{
/// <summary>
/// NativeSortedList
/// </summary>
private readonly NativeSortedList<TKey, TValue> _nativeSortedList;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeSortedList">NativeSortedList</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal KeyCollection(NativeSortedList<TKey, TValue> nativeSortedList) => _nativeSortedList = nativeSortedList;
/// <summary>
/// Get enumerator
/// </summary>
/// <returns>Enumerator</returns>
public Enumerator GetEnumerator() => new(_nativeSortedList);
/// <summary>
/// Enumerator
/// </summary>
public struct Enumerator
{
/// <summary>
/// NativeSortedList
/// </summary>
private readonly NativeSortedList<TKey, TValue> _nativeSortedList;
/// <summary>
/// Current
/// </summary>
private TKey _current;
/// <summary>
/// Index
/// </summary>
private int _index;
/// <summary>
/// Version
/// </summary>
private readonly int _version;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeSortedList">NativeSortedList</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(NativeSortedList<TKey, TValue> nativeSortedList)
{
_nativeSortedList = nativeSortedList;
_current = default;
_index = 0;
_version = _nativeSortedList._handle->Version;
}
/// <summary>
/// Move next
/// </summary>
/// <returns>Moved</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
if (_version != _nativeSortedList._handle->Version)
throw new InvalidOperationException("EnumFailedVersion");
if ((uint)_index < (uint)_nativeSortedList._handle->Size)
{
_current = _nativeSortedList._handle->Buckets[_index];
++_index;
return true;
}
_index = _nativeSortedList._handle->Size + 1;
return false;
}
/// <summary>
/// Current
/// </summary>
public TKey Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _current;
}
}
}
/// <summary>
/// Value collection
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public readonly struct ValueCollection
{
/// <summary>
/// NativeSortedList
/// </summary>
private readonly NativeSortedList<TKey, TValue> _nativeSortedList;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeSortedList">NativeSortedList</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ValueCollection(NativeSortedList<TKey, TValue> nativeSortedList) => _nativeSortedList = nativeSortedList;
/// <summary>
/// Get enumerator
/// </summary>
/// <returns>Enumerator</returns>
public Enumerator GetEnumerator() => new(_nativeSortedList);
/// <summary>
/// Enumerator
/// </summary>
public struct Enumerator
{
/// <summary>
/// NativeSortedList
/// </summary>
private readonly NativeSortedList<TKey, TValue> _nativeSortedList;
/// <summary>
/// Current
/// </summary>
private TValue _current;
/// <summary>
/// Index
/// </summary>
private int _index;
/// <summary>
/// Version
/// </summary>
private readonly int _version;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeSortedList">NativeSortedList</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(NativeSortedList<TKey, TValue> nativeSortedList)
{
_nativeSortedList = nativeSortedList;
_current = default;
_index = 0;
_version = _nativeSortedList._handle->Version;
}
/// <summary>
/// Move next
/// </summary>
/// <returns>Moved</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
if (_version != _nativeSortedList._handle->Version)
throw new InvalidOperationException("EnumFailedVersion");
if ((uint)_index < (uint)_nativeSortedList._handle->Size)
{
_current = _nativeSortedList._handle->Entries[_index];
++_index;
return true;
}
_index = _nativeSortedList._handle->Size + 1;
return false;
}
/// <summary>
/// Current
/// </summary>
public TValue Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _current;
}
}
}
}
}

View File

@@ -0,0 +1,973 @@
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if NET5_0_OR_GREATER
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native sortedSet
/// </summary>
/// <typeparam name="T">Type</typeparam>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeSortedSet<T> : IDisposable, IEquatable<NativeSortedSet<T>> where T : unmanaged, IComparable<T>
{
/// <summary>
/// Handle
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct NativeSortedSetHandle
{
/// <summary>
/// Root
/// </summary>
public Node* Root;
/// <summary>
/// Count
/// </summary>
public int Count;
/// <summary>
/// Version
/// </summary>
public int Version;
/// <summary>
/// Node pool
/// </summary>
public NativeMemoryPool NodePool;
}
/// <summary>
/// Handle
/// </summary>
private readonly NativeSortedSetHandle* _handle;
/// <summary>
/// Structure
/// </summary>
/// <param name="size">MemoryPool size</param>
/// <param name="maxFreeSlabs">MemoryPool maxFreeSlabs</param>
public NativeSortedSet(int size, int maxFreeSlabs)
{
var nodePool = new NativeMemoryPool(size, sizeof(Node), maxFreeSlabs);
_handle = (NativeSortedSetHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeSortedSetHandle));
_handle->Root = null;
_handle->Count = 0;
_handle->Version = 0;
_handle->NodePool = nodePool;
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle != null;
/// <summary>
/// Is empty
/// </summary>
public bool IsEmpty => _handle->Count == 0;
/// <summary>
/// Count
/// </summary>
public int Count => _handle->Count;
/// <summary>
/// Min
/// </summary>
public T? Min
{
get
{
if (_handle->Root == null)
return default;
var current = _handle->Root;
while (current->Left != null)
current = current->Left;
return current->Item;
}
}
/// <summary>
/// Max
/// </summary>
public T? Max
{
get
{
if (_handle->Root == null)
return default;
var current = _handle->Root;
while (current->Right != null)
current = current->Right;
return current->Item;
}
}
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeSortedSet<T> other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeSortedSet<T> nativeSortedSet && nativeSortedSet == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"NativeSortedSet<{typeof(T).Name}>";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeSortedSet<T> left, NativeSortedSet<T> right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeSortedSet<T> left, NativeSortedSet<T> right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_handle == null)
return;
_handle->NodePool.Dispose();
NativeMemoryAllocator.Free(_handle);
}
/// <summary>
/// Clear
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
if (_handle->Root != null)
{
var nodeStack = new NativeStack<nint>(2 * Log2(_handle->Count + 1));
nodeStack.Push((nint)_handle->Root);
while (nodeStack.TryPop(out var node))
{
var currentNode = (Node*)node;
if (currentNode->Left != null)
nodeStack.Push((nint)currentNode->Left);
if (currentNode->Right != null)
nodeStack.Push((nint)currentNode->Right);
_handle->NodePool.Return(currentNode);
}
nodeStack.Dispose();
}
_handle->Root = null;
_handle->Count = 0;
++_handle->Version;
}
/// <summary>
/// Add
/// </summary>
/// <param name="item">Item</param>
/// <returns>Added</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Add(in T item)
{
if (_handle->Root == null)
{
_handle->Root = (Node*)_handle->NodePool.Rent();
_handle->Root->Item = item;
_handle->Root->Left = null;
_handle->Root->Right = null;
_handle->Root->Color = NodeColor.Black;
_handle->Count = 1;
_handle->Version++;
return true;
}
var current = _handle->Root;
Node* parent = null;
Node* grandParent = null;
Node* greatGrandParent = null;
_handle->Version++;
var order = 0;
while (current != null)
{
order = item.CompareTo(current->Item);
if (order == 0)
{
_handle->Root->ColorBlack();
return false;
}
if (current->Is4Node)
{
current->Split4Node();
if (Node.IsNonNullRed(parent))
InsertionBalance(current, parent, grandParent, greatGrandParent);
}
greatGrandParent = grandParent;
grandParent = parent;
parent = current;
current = order < 0 ? current->Left : current->Right;
}
var node = (Node*)_handle->NodePool.Rent();
node->Item = item;
node->Left = null;
node->Right = null;
node->Color = NodeColor.Red;
if (order > 0)
parent->Right = node;
else
parent->Left = node;
if (parent->IsRed)
InsertionBalance(node, parent, grandParent, greatGrandParent);
_handle->Root->ColorBlack();
++_handle->Count;
return true;
}
/// <summary>
/// Add
/// </summary>
/// <param name="equalValue">Equal value</param>
/// <param name="actualValue">Actual value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(in T equalValue, in T actualValue)
{
var node = FindNode(equalValue);
if (node == null)
{
Add(actualValue);
}
else
{
node->Item = actualValue;
_handle->Version++;
}
}
/// <summary>
/// Remove
/// </summary>
/// <param name="item">Item</param>
/// <returns>Removed</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(in T item)
{
if (_handle->Root == null)
return false;
_handle->Version++;
var current = _handle->Root;
Node* parent = null;
Node* grandParent = null;
Node* match = null;
Node* parentOfMatch = null;
var foundMatch = false;
while (current != null)
{
if (current->Is2Node)
{
if (parent == null)
{
current->ColorRed();
}
else
{
var sibling = parent->GetSibling(current);
if (sibling->IsRed)
{
if (parent->Right == sibling)
parent->RotateLeft();
else
parent->RotateRight();
parent->ColorRed();
sibling->ColorBlack();
ReplaceChildOrRoot(grandParent, parent, sibling);
grandParent = sibling;
if (parent == match)
parentOfMatch = sibling;
sibling = parent->GetSibling(current);
}
if (sibling->Is2Node)
{
parent->Merge2Nodes();
}
else
{
var newGrandParent = parent->Rotate(parent->GetRotation(current, sibling));
newGrandParent->Color = parent->Color;
parent->ColorBlack();
current->ColorRed();
ReplaceChildOrRoot(grandParent, parent, newGrandParent);
if (parent == match)
parentOfMatch = newGrandParent;
}
}
}
var order = foundMatch ? -1 : item.CompareTo(current->Item);
if (order == 0)
{
foundMatch = true;
match = current;
parentOfMatch = parent;
}
grandParent = parent;
parent = current;
current = order < 0 ? current->Left : current->Right;
}
if (match != null)
{
ReplaceNode(match, parentOfMatch, parent, grandParent);
--_handle->Count;
_handle->NodePool.Return(match);
}
if (_handle->Root != null)
_handle->Root->ColorBlack();
return foundMatch;
}
/// <summary>
/// Remove
/// </summary>
/// <param name="equalValue">Equal value</param>
/// <param name="actualValue">Actual value</param>
/// <returns>Removed</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Remove(in T equalValue, out T actualValue)
{
if (_handle->Root == null)
{
actualValue = default;
return false;
}
_handle->Version++;
var current = _handle->Root;
Node* parent = null;
Node* grandParent = null;
Node* match = null;
Node* parentOfMatch = null;
var foundMatch = false;
while (current != null)
{
if (current->Is2Node)
{
if (parent == null)
{
current->ColorRed();
}
else
{
var sibling = parent->GetSibling(current);
if (sibling->IsRed)
{
if (parent->Right == sibling)
parent->RotateLeft();
else
parent->RotateRight();
parent->ColorRed();
sibling->ColorBlack();
ReplaceChildOrRoot(grandParent, parent, sibling);
grandParent = sibling;
if (parent == match)
parentOfMatch = sibling;
sibling = parent->GetSibling(current);
}
if (sibling->Is2Node)
{
parent->Merge2Nodes();
}
else
{
var newGrandParent = parent->Rotate(parent->GetRotation(current, sibling));
newGrandParent->Color = parent->Color;
parent->ColorBlack();
current->ColorRed();
ReplaceChildOrRoot(grandParent, parent, newGrandParent);
if (parent == match)
parentOfMatch = newGrandParent;
}
}
}
var order = foundMatch ? -1 : equalValue.CompareTo(current->Item);
if (order == 0)
{
foundMatch = true;
match = current;
parentOfMatch = parent;
}
grandParent = parent;
parent = current;
current = order < 0 ? current->Left : current->Right;
}
if (match != null)
{
actualValue = match->Item;
ReplaceNode(match, parentOfMatch, parent, grandParent);
--_handle->Count;
_handle->NodePool.Return(match);
}
else
{
actualValue = default;
}
if (_handle->Root != null)
_handle->Root->ColorBlack();
return foundMatch;
}
/// <summary>
/// Contains
/// </summary>
/// <param name="item">Item</param>
/// <returns>Contains</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(in T item) => FindNode(item) != null;
/// <summary>
/// Try to get the actual value
/// </summary>
/// <param name="equalValue">Equal value</param>
/// <param name="actualValue">Actual value</param>
/// <returns>Got</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(in T equalValue, out T actualValue)
{
var node = FindNode(equalValue);
if (node != null)
{
actualValue = node->Item;
return true;
}
actualValue = default;
return false;
}
/// <summary>
/// Insertion balance
/// </summary>
/// <param name="current">Current</param>
/// <param name="parent">Parent</param>
/// <param name="grandParent">Grand parent</param>
/// <param name="greatGrandParent">GreatGrand parent</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void InsertionBalance(Node* current, Node* parent, Node* grandParent, Node* greatGrandParent)
{
var parentIsOnRight = grandParent->Right == parent;
var currentIsOnRight = parent->Right == current;
Node* newChildOfGreatGrandParent;
if (parentIsOnRight == currentIsOnRight)
newChildOfGreatGrandParent = currentIsOnRight ? grandParent->RotateLeft() : grandParent->RotateRight();
else
newChildOfGreatGrandParent = currentIsOnRight ? grandParent->RotateLeftRight() : grandParent->RotateRightLeft();
grandParent->ColorRed();
newChildOfGreatGrandParent->ColorBlack();
ReplaceChildOrRoot(greatGrandParent, grandParent, newChildOfGreatGrandParent);
}
/// <summary>
/// Replace child or root
/// </summary>
/// <param name="parent">Parent</param>
/// <param name="child">Child</param>
/// <param name="newChild">New child</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReplaceChildOrRoot(Node* parent, Node* child, Node* newChild)
{
if (parent != null)
parent->ReplaceChild(child, newChild);
else
_handle->Root = newChild;
}
/// <summary>
/// Replace node
/// </summary>
/// <param name="match">Match</param>
/// <param name="parentOfMatch">Parent of match</param>
/// <param name="successor">Successor</param>
/// <param name="parentOfSuccessor">Parent of successor</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReplaceNode(Node* match, Node* parentOfMatch, Node* successor, Node* parentOfSuccessor)
{
if (successor == match)
{
successor = match->Left;
}
else
{
if (successor->Right != null)
successor->Right->ColorBlack();
if (parentOfSuccessor != match)
{
parentOfSuccessor->Left = successor->Right;
successor->Right = match->Right;
}
successor->Left = match->Left;
}
if (successor != null)
successor->Color = match->Color;
ReplaceChildOrRoot(parentOfMatch, match, successor);
}
/// <summary>
/// Find node
/// </summary>
/// <param name="item">Item</param>
/// <returns>Node</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Node* FindNode(in T item)
{
var current = _handle->Root;
while (current != null)
{
var order = item.CompareTo(current->Item);
if (order == 0)
return current;
current = order < 0 ? current->Left : current->Right;
}
return null;
}
/// <summary>
/// Log2
/// </summary>
/// <param name="value">Value</param>
/// <returns>Log2</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int Log2(int value) => BitOperationsHelpers.Log2(value);
/// <summary>
/// Node
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct Node
{
/// <summary>
/// Is non null red
/// </summary>
/// <param name="node">Node</param>
/// <returns>Is non null red</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsNonNullRed(Node* node) => node != null && node->IsRed;
/// <summary>
/// Is null or black
/// </summary>
/// <param name="node">Node</param>
/// <returns>Is null or black</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsNullOrBlack(Node* node) => node == null || node->IsBlack;
/// <summary>
/// Item
/// </summary>
public T Item
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set;
}
/// <summary>
/// Left
/// </summary>
public Node* Left
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set;
}
/// <summary>
/// Right
/// </summary>
public Node* Right
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set;
}
/// <summary>
/// Color
/// </summary>
public NodeColor Color
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set;
}
/// <summary>
/// Is black
/// </summary>
private bool IsBlack
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Color == NodeColor.Black;
}
/// <summary>
/// Is red
/// </summary>
public bool IsRed
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Color == NodeColor.Red;
}
/// <summary>
/// Is 2 node
/// </summary>
public bool Is2Node
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => IsBlack && IsNullOrBlack(Left) && IsNullOrBlack(Right);
}
/// <summary>
/// Is 4 node
/// </summary>
public bool Is4Node
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => IsNonNullRed(Left) && IsNonNullRed(Right);
}
/// <summary>
/// Set color to black
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ColorBlack() => Color = NodeColor.Black;
/// <summary>
/// Set color to red
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ColorRed() => Color = NodeColor.Red;
/// <summary>
/// Get rotation
/// </summary>
/// <param name="current">Current</param>
/// <param name="sibling">Sibling</param>
/// <returns>Rotation</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TreeRotation GetRotation(Node* current, Node* sibling)
{
var currentIsLeftChild = Left == current;
return IsNonNullRed(sibling->Left) ? currentIsLeftChild ? TreeRotation.RightLeft : TreeRotation.Right : currentIsLeftChild ? TreeRotation.Left : TreeRotation.LeftRight;
}
/// <summary>
/// Get sibling
/// </summary>
/// <param name="node">Node</param>
/// <returns>Sibling</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Node* GetSibling(Node* node) => node == Left ? Right : Left;
/// <summary>
/// Split 4 node
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Split4Node()
{
ColorRed();
Left->ColorBlack();
Right->ColorBlack();
}
/// <summary>
/// Rotate
/// </summary>
/// <param name="rotation">Rotation</param>
/// <returns>Node</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Node* Rotate(TreeRotation rotation)
{
Node* removeRed;
switch (rotation)
{
case TreeRotation.Right:
removeRed = Left->Left;
removeRed->ColorBlack();
return RotateRight();
case TreeRotation.Left:
removeRed = Right->Right;
removeRed->ColorBlack();
return RotateLeft();
case TreeRotation.RightLeft:
return RotateRightLeft();
case TreeRotation.LeftRight:
return RotateLeftRight();
default:
return null;
}
}
/// <summary>
/// Rotate left
/// </summary>
/// <returns>Node</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Node* RotateLeft()
{
var child = Right;
Right = child->Left;
child->Left = (Node*)Unsafe.AsPointer(ref this);
return child;
}
/// <summary>
/// Rotate left right
/// </summary>
/// <returns>Node</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Node* RotateLeftRight()
{
var child = Left;
var grandChild = child->Right;
Left = grandChild->Right;
grandChild->Right = (Node*)Unsafe.AsPointer(ref this);
child->Right = grandChild->Left;
grandChild->Left = child;
return grandChild;
}
/// <summary>
/// Rotate right
/// </summary>
/// <returns>Node</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Node* RotateRight()
{
var child = Left;
Left = child->Right;
child->Right = (Node*)Unsafe.AsPointer(ref this);
return child;
}
/// <summary>
/// Rotate right left
/// </summary>
/// <returns>Node</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Node* RotateRightLeft()
{
var child = Right;
var grandChild = child->Left;
Right = grandChild->Left;
grandChild->Left = (Node*)Unsafe.AsPointer(ref this);
child->Left = grandChild->Right;
grandChild->Right = child;
return grandChild;
}
/// <summary>
/// Merge 2 nodes
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Merge2Nodes()
{
ColorBlack();
Left->ColorRed();
Right->ColorRed();
}
/// <summary>
/// Replace child
/// </summary>
/// <param name="child">Child</param>
/// <param name="newChild">New child</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReplaceChild(Node* child, Node* newChild)
{
if (Left == child)
Left = newChild;
else
Right = newChild;
}
}
/// <summary>
/// Empty
/// </summary>
public static NativeSortedSet<T> Empty => new();
/// <summary>
/// Get enumerator
/// </summary>
/// <returns>Enumerator</returns>
public Enumerator GetEnumerator() => new(this);
/// <summary>
/// Enumerator
/// </summary>
public struct Enumerator : IDisposable
{
/// <summary>
/// NativeHashSet
/// </summary>
private readonly NativeSortedSet<T> _nativeSortedSet;
/// <summary>
/// Version
/// </summary>
private readonly int _version;
/// <summary>
/// Node stack
/// </summary>
private readonly NativeStack<nint> _nodeStack;
/// <summary>
/// Current
/// </summary>
private Node* _currentNode;
/// <summary>
/// Current
/// </summary>
private T _current;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeSortedSet">NativeSortedSet</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(NativeSortedSet<T> nativeSortedSet)
{
_nativeSortedSet = nativeSortedSet;
_version = nativeSortedSet._handle->Version;
_nodeStack = new NativeStack<nint>(2 * Log2(nativeSortedSet.Count + 1));
_currentNode = null;
_current = default;
var node = _nativeSortedSet._handle->Root;
while (node != null)
{
var next = node->Left;
_nodeStack.Push((nint)node);
node = next;
}
}
/// <summary>
/// Move next
/// </summary>
/// <returns>Moved</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
if (_version != _nativeSortedSet._handle->Version)
throw new InvalidOperationException("EnumFailedVersion");
if (!_nodeStack.TryPop(out var result))
{
_currentNode = null;
_current = default;
return false;
}
_currentNode = (Node*)result;
_current = _currentNode->Item;
var node = _currentNode->Right;
while (node != null)
{
var next = node->Left;
_nodeStack.Push((nint)node);
node = next;
}
return true;
}
/// <summary>
/// Current
/// </summary>
public T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _current;
}
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose() => _nodeStack.Dispose();
}
/// <summary>
/// Node color
/// </summary>
private enum NodeColor : byte
{
Black,
Red
}
/// <summary>
/// Tree rotation
/// </summary>
private enum TreeRotation : byte
{
Left,
LeftRight,
Right,
RightLeft
}
}
}

View File

@@ -0,0 +1,425 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if UNITY_2021_3_OR_NEWER || GODOT
using System;
#endif
#pragma warning disable CA2208
#pragma warning disable CS8632
// ReSharper disable ALL
namespace NativeCollections
{
/// <summary>
/// Native stack
/// </summary>
/// <typeparam name="T">Type</typeparam>
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct NativeStack<T> : IDisposable, IEquatable<NativeStack<T>> where T : unmanaged
{
/// <summary>
/// Handle
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct NativeStackHandle
{
/// <summary>
/// Array
/// </summary>
public T* Array;
/// <summary>
/// Length
/// </summary>
public int Length;
/// <summary>
/// Size
/// </summary>
public int Size;
/// <summary>
/// Version
/// </summary>
public int Version;
}
/// <summary>
/// Handle
/// </summary>
private readonly NativeStackHandle* _handle;
/// <summary>
/// Structure
/// </summary>
/// <param name="capacity">Capacity</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NativeStack(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "MustBeNonNegative");
if (capacity < 4)
capacity = 4;
_handle = (NativeStackHandle*)NativeMemoryAllocator.Alloc((uint)sizeof(NativeStackHandle));
_handle->Array = (T*)NativeMemoryAllocator.Alloc((uint)(capacity * sizeof(T)));
_handle->Length = capacity;
_handle->Size = 0;
_handle->Version = 0;
}
/// <summary>
/// Is created
/// </summary>
public bool IsCreated => _handle != null;
/// <summary>
/// Is empty
/// </summary>
public bool IsEmpty => _handle->Size == 0;
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public ref T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _handle->Array[index];
}
/// <summary>
/// Get reference
/// </summary>
/// <param name="index">Index</param>
public ref T this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _handle->Array[index];
}
/// <summary>
/// Count
/// </summary>
public int Count => _handle->Size;
/// <summary>
/// Equals
/// </summary>
/// <param name="other">Other</param>
/// <returns>Equals</returns>
public bool Equals(NativeStack<T> other) => other == this;
/// <summary>
/// Equals
/// </summary>
/// <param name="obj">object</param>
/// <returns>Equals</returns>
public override bool Equals(object? obj) => obj is NativeStack<T> nativeStack && nativeStack == this;
/// <summary>
/// Get hashCode
/// </summary>
/// <returns>HashCode</returns>
public override int GetHashCode() => (int)(nint)_handle;
/// <summary>
/// To string
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"NativeStack<{typeof(T).Name}>";
/// <summary>
/// Equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Equals</returns>
public static bool operator ==(NativeStack<T> left, NativeStack<T> right) => left._handle == right._handle;
/// <summary>
/// Not equals
/// </summary>
/// <param name="left">Left</param>
/// <param name="right">Right</param>
/// <returns>Not equals</returns>
public static bool operator !=(NativeStack<T> left, NativeStack<T> right) => left._handle != right._handle;
/// <summary>
/// Dispose
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_handle == null)
return;
NativeMemoryAllocator.Free(_handle->Array);
NativeMemoryAllocator.Free(_handle);
}
/// <summary>
/// Clear
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
_handle->Size = 0;
_handle->Version++;
}
/// <summary>
/// Push
/// </summary>
/// <param name="item">Item</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Push(in T item)
{
var size = _handle->Size;
if ((uint)size < (uint)_handle->Length)
{
_handle->Array[size] = item;
_handle->Version++;
_handle->Size = size + 1;
}
else
{
Grow(_handle->Size + 1);
_handle->Array[_handle->Size] = item;
_handle->Version++;
_handle->Size++;
}
}
/// <summary>
/// Try push
/// </summary>
/// <param name="item">Item</param>
/// <returns>Pushed</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPush(in T item)
{
var size = _handle->Size;
if ((uint)size < (uint)_handle->Length)
{
_handle->Array[size] = item;
_handle->Version++;
_handle->Size = size + 1;
return true;
}
return false;
}
/// <summary>
/// Pop
/// </summary>
/// <returns>Item</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Pop()
{
var size = _handle->Size - 1;
if ((uint)size >= (uint)_handle->Length)
throw new InvalidOperationException("EmptyStack");
_handle->Version++;
_handle->Size = size;
var item = _handle->Array[size];
return item;
}
/// <summary>
/// Try pop
/// </summary>
/// <param name="result">Item</param>
/// <returns>Popped</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T result)
{
var size = _handle->Size - 1;
if ((uint)size >= (uint)_handle->Length)
{
result = default;
return false;
}
_handle->Version++;
_handle->Size = size;
result = _handle->Array[size];
return true;
}
/// <summary>
/// Peek
/// </summary>
/// <returns>Item</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Peek()
{
var size = _handle->Size - 1;
return (uint)size >= (uint)_handle->Length ? throw new InvalidOperationException("EmptyStack") : _handle->Array[size];
}
/// <summary>
/// Try peek
/// </summary>
/// <param name="result">Item</param>
/// <returns>Peeked</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPeek(out T result)
{
var size = _handle->Size - 1;
if ((uint)size >= (uint)_handle->Length)
{
result = default;
return false;
}
result = _handle->Array[size];
return true;
}
/// <summary>
/// Ensure capacity
/// </summary>
/// <param name="capacity">Capacity</param>
/// <returns>New capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int EnsureCapacity(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "MustBeNonNegative");
if (_handle->Length < capacity)
Grow(capacity);
return _handle->Length;
}
/// <summary>
/// Trim excess
/// </summary>
/// <returns>New capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int TrimExcess()
{
var threshold = (int)(_handle->Length * 0.9);
if (_handle->Size < threshold)
SetCapacity(_handle->Size);
return _handle->Length;
}
/// <summary>
/// Set capacity
/// </summary>
/// <param name="capacity">Capacity</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetCapacity(int capacity)
{
var newArray = (T*)NativeMemoryAllocator.Alloc((uint)(capacity * sizeof(T)));
if (_handle->Size > 0)
Unsafe.CopyBlockUnaligned(newArray, _handle->Array, (uint)(_handle->Length * sizeof(T)));
NativeMemoryAllocator.Free(_handle->Array);
_handle->Array = newArray;
_handle->Length = capacity;
}
/// <summary>
/// Grow
/// </summary>
/// <param name="capacity">Capacity</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Grow(int capacity)
{
var newCapacity = 2 * _handle->Length;
if ((uint)newCapacity > 2147483591)
newCapacity = 2147483591;
var expected = _handle->Length + 4;
newCapacity = newCapacity > expected ? newCapacity : expected;
if (newCapacity < capacity)
newCapacity = capacity;
SetCapacity(newCapacity);
}
/// <summary>
/// Empty
/// </summary>
public static NativeStack<T> Empty => new();
/// <summary>
/// Get enumerator
/// </summary>
/// <returns>Enumerator</returns>
public Enumerator GetEnumerator() => new(this);
/// <summary>
/// Enumerator
/// </summary>
public struct Enumerator
{
/// <summary>
/// NativeStack
/// </summary>
private readonly NativeStack<T> _nativeStack;
/// <summary>
/// Version
/// </summary>
private readonly int _version;
/// <summary>
/// Index
/// </summary>
private int _index;
/// <summary>
/// Current element
/// </summary>
private T _currentElement;
/// <summary>
/// Structure
/// </summary>
/// <param name="nativeStack">NativeStack</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(in NativeStack<T> nativeStack)
{
_nativeStack = nativeStack;
_version = nativeStack._handle->Version;
_index = -2;
_currentElement = default;
}
/// <summary>
/// Move next
/// </summary>
/// <returns>Moved</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
if (_version != _nativeStack._handle->Version)
throw new InvalidOperationException("EnumFailedVersion");
bool returned;
if (_index == -2)
{
_index = _nativeStack._handle->Size - 1;
returned = _index >= 0;
if (returned)
_currentElement = _nativeStack._handle->Array[_index];
return returned;
}
if (_index == -1)
return false;
returned = --_index >= 0;
_currentElement = returned ? _nativeStack._handle->Array[_index] : default;
return returned;
}
/// <summary>
/// Current
/// </summary>
public T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _index < 0 ? throw new InvalidOperationException(_index == -1 ? "EnumNotStarted" : "EnumEnded") : _currentElement;
}
}
}
}

View File

@@ -0,0 +1,3 @@
# NativeCollections
This project is a pure C# native collections for (Unity/Godot/.NET)

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,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,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,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,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,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,68 @@
namespace Fantasy.DataStructure.SkipTable
{
/// <summary>
/// 跳跃表节点。
/// </summary>
/// <typeparam name="TValue">节点的值的类型。</typeparam>
public class SkipTableNode<TValue>
{
/// <summary>
/// 节点在跳跃表中的索引。
/// </summary>
public int Index;
/// <summary>
/// 节点的主键。
/// </summary>
public long Key;
/// <summary>
/// 节点的排序键。
/// </summary>
public long SortKey;
/// <summary>
/// 节点的副键。
/// </summary>
public long ViceKey;
/// <summary>
/// 节点存储的值。
/// </summary>
public TValue Value;
/// <summary>
/// 指向左侧节点的引用。
/// </summary>
public SkipTableNode<TValue> Left;
/// <summary>
/// 指向右侧节点的引用。
/// </summary>
public SkipTableNode<TValue> Right;
/// <summary>
/// 指向下一层节点的引用。
/// </summary>
public SkipTableNode<TValue> Down;
/// <summary>
/// 初始化跳跃表节点的新实例。
/// </summary>
/// <param name="sortKey">节点的排序键。</param>
/// <param name="viceKey">节点的副键。</param>
/// <param name="key">节点的主键。</param>
/// <param name="value">节点存储的值。</param>
/// <param name="index">节点在跳跃表中的索引。</param>
/// <param name="l">指向左侧节点的引用。</param>
/// <param name="r">指向右侧节点的引用。</param>
/// <param name="d">指向下一层节点的引用。</param>
public SkipTableNode(long sortKey, long viceKey, long key, TValue value, int index,
SkipTableNode<TValue> l,
SkipTableNode<TValue> r,
SkipTableNode<TValue> d)
{
Left = l;
Right = r;
Down = d;
Value = value;
Key = key;
Index = index;
SortKey = sortKey;
ViceKey = viceKey;
}
}
}

View File

@@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using Fantasy.Pool;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
namespace Fantasy.Async
{
/// <summary>
/// 协程锁专用的对象池
/// </summary>
public sealed class CoroutineLockPool : PoolCore<CoroutineLock>
{
/// <summary>
/// 协程锁专用的对象池的构造函数
/// </summary>
public CoroutineLockPool() : base(2000) { }
}
/// <summary>
/// 协程锁
/// </summary>
public sealed class CoroutineLock : IPool, IDisposable
{
private Scene _scene;
private CoroutineLockComponent _coroutineLockComponent;
private readonly Dictionary<long, CoroutineLockQueue> _queue = new Dictionary<long, CoroutineLockQueue>();
/// <summary>
/// 表示是否是对象池中创建的
/// </summary>
private bool _isPool;
/// <summary>
/// 协程锁的类型
/// </summary>
public long CoroutineLockType { get; private set; }
internal void Initialize(CoroutineLockComponent coroutineLockComponent, ref long coroutineLockType)
{
_scene = coroutineLockComponent.Scene;
CoroutineLockType = coroutineLockType;
_coroutineLockComponent = coroutineLockComponent;
}
/// <summary>
/// 销毁协程锁,如果调用了该方法,所有使用当前协程锁等待的逻辑会按照顺序释放锁。
/// </summary>
public void Dispose()
{
foreach (var (_, coroutineLockQueue) in _queue)
{
while (TryCoroutineLockQueueDequeue(coroutineLockQueue)) { }
}
_queue.Clear();
_scene = null;
CoroutineLockType = 0;
_coroutineLockComponent = null;
}
/// <summary>
/// 等待上一个任务完成
/// </summary>
/// <param name="coroutineLockQueueKey">需要等待的Id</param>
/// <param name="tag">用于查询协程锁的标记,可不传入,只有在超时的时候排查是哪个锁超时时使用</param>
/// <param name="timeOut">等待多久会超时,当到达设定的时候会把当前锁给按照超时处理</param>
/// <returns></returns>
public async FTask<WaitCoroutineLock> Wait(long coroutineLockQueueKey, string tag = null, int timeOut = 30000)
{
var waitCoroutineLock = _coroutineLockComponent.WaitCoroutineLockPool.Rent(this, ref coroutineLockQueueKey, tag, timeOut);
if (!_queue.TryGetValue(coroutineLockQueueKey, out var queue))
{
queue = _coroutineLockComponent.CoroutineLockQueuePool.Rent();
_queue.Add(coroutineLockQueueKey, queue);
return waitCoroutineLock;
}
queue.Enqueue(waitCoroutineLock);
return await waitCoroutineLock.Tcs;
}
/// <summary>
/// 按照先入先出的顺序,释放最早的一个协程锁
/// </summary>
/// <param name="coroutineLockQueueKey"></param>
public void Release(long coroutineLockQueueKey)
{
if (!_queue.TryGetValue(coroutineLockQueueKey, out var coroutineLockQueue))
{
return;
}
if (!TryCoroutineLockQueueDequeue(coroutineLockQueue))
{
_queue.Remove(coroutineLockQueueKey);
}
}
private bool TryCoroutineLockQueueDequeue(CoroutineLockQueue coroutineLockQueue)
{
if (!coroutineLockQueue.TryDequeue(out var waitCoroutineLock))
{
_coroutineLockComponent.CoroutineLockQueuePool.Return(coroutineLockQueue);
return false;
}
if (waitCoroutineLock.TimerId != 0)
{
_scene.TimerComponent.Net.Remove(waitCoroutineLock.TimerId);
}
try
{
// 放到下一帧执行,如果不这样会导致逻辑的顺序不正常。
_scene.ThreadSynchronizationContext.Post(waitCoroutineLock.SetResult);
}
catch (Exception e)
{
Log.Error($"Error in disposing CoroutineLock: {e}");
}
return true;
}
/// <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,100 @@
using System.Collections.Generic;
using Fantasy.Entitas;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
namespace Fantasy.Async
{
/// <summary>
/// 协程锁组件
/// </summary>
public class CoroutineLockComponent : Entity
{
private long _lockId;
private CoroutineLockPool _coroutineLockPool;
internal WaitCoroutineLockPool WaitCoroutineLockPool { get; private set; }
internal CoroutineLockQueuePool CoroutineLockQueuePool { get; private set; }
private readonly Dictionary<long, CoroutineLock> _coroutineLocks = new Dictionary<long, CoroutineLock>();
internal CoroutineLockComponent Initialize()
{
_coroutineLockPool = new CoroutineLockPool();
CoroutineLockQueuePool = new CoroutineLockQueuePool();
WaitCoroutineLockPool = new WaitCoroutineLockPool(this);
return this;
}
internal long LockId => ++_lockId;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public override void Dispose()
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
{
if (IsDisposed)
{
return;
}
_lockId = 0;
base.Dispose();
}
/// <summary>
/// 创建一个新的协程锁
/// 使用这个方法创建的协程锁需要手动释放管理CoroutineLock。
/// 不会再CoroutineLockComponent理进行管理。
/// </summary>
/// <param name="coroutineLockType"></param>
/// <returns></returns>
public CoroutineLock Create(long coroutineLockType)
{
var coroutineLock = _coroutineLockPool.Rent();
coroutineLock.Initialize(this, ref coroutineLockType);
return coroutineLock;
}
/// <summary>
/// 请求一个协程锁。
/// 使用这个方法创建的协程锁会自动释放CoroutineLockQueueType。
/// </summary>
/// <param name="coroutineLockType">锁类型</param>
/// <param name="coroutineLockQueueKey">锁队列Id</param>
/// <param name="tag">当某些锁超时需要一个标记来方便排查问题正常的情况下这个默认为null就可以。</param>
/// <param name="time">设置锁的超时时间,让超过设置的时间会触发超时,保证锁不会因为某一个锁一直不解锁导致卡住的问题。</param>
/// <returns>
/// 返回的WaitCoroutineLock通过Dispose来解除这个锁、建议用using来保住这个锁。
/// 也可以返回的WaitCoroutineLock通过CoroutineLockComponent.UnLock来解除这个锁。
/// </returns>
public FTask<WaitCoroutineLock> Wait(long coroutineLockType, long coroutineLockQueueKey, string tag = null, int time = 30000)
{
if (!_coroutineLocks.TryGetValue(coroutineLockType, out var coroutineLock))
{
coroutineLock = _coroutineLockPool.Rent();
coroutineLock.Initialize(this, ref coroutineLockType);
_coroutineLocks.Add(coroutineLockType, coroutineLock);
}
return coroutineLock.Wait(coroutineLockQueueKey, tag, time);
}
/// <summary>
/// 解除一个协程锁。
/// </summary>
/// <param name="coroutineLockType"></param>
/// <param name="coroutineLockQueueKey"></param>
public void Release(int coroutineLockType, long coroutineLockQueueKey)
{
if (IsDisposed)
{
return;
}
if (!_coroutineLocks.TryGetValue(coroutineLockType, out var coroutineLock))
{
return;
}
coroutineLock.Release(coroutineLockQueueKey);
}
}
}

View File

@@ -0,0 +1,35 @@
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
using System.Collections.Generic;
using Fantasy.Pool;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
namespace Fantasy.Async
{
internal sealed class CoroutineLockQueuePool : PoolCore<CoroutineLockQueue>
{
public CoroutineLockQueuePool() : base(2000) { }
}
internal sealed class CoroutineLockQueue : Queue<WaitCoroutineLock>, IPool
{
private bool _isPool;
/// <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,146 @@
using System;
using Fantasy.Event;
using Fantasy.Pool;
#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.Async
{
internal sealed class WaitCoroutineLockPool : PoolCore<WaitCoroutineLock>
{
private readonly Scene _scene;
private readonly CoroutineLockComponent _coroutineLockComponent;
public WaitCoroutineLockPool(CoroutineLockComponent coroutineLockComponent) : base(2000)
{
_scene = coroutineLockComponent.Scene;
_coroutineLockComponent = coroutineLockComponent;
}
public WaitCoroutineLock Rent(CoroutineLock coroutineLock, ref long coroutineLockQueueKey, string tag = null, int timeOut = 30000)
{
var timerId = 0L;
var lockId = _coroutineLockComponent.LockId;
var waitCoroutineLock = _coroutineLockComponent.WaitCoroutineLockPool.Rent();
if (timeOut > 0)
{
timerId = _scene.TimerComponent.Net.OnceTimer(timeOut, new CoroutineLockTimeout(ref lockId, waitCoroutineLock));
}
waitCoroutineLock.Initialize(coroutineLock, this, ref coroutineLockQueueKey, ref timerId, ref lockId, tag);
return waitCoroutineLock;
}
}
internal struct CoroutineLockTimeout
{
public readonly long LockId;
public readonly WaitCoroutineLock WaitCoroutineLock;
public CoroutineLockTimeout(ref long lockId, WaitCoroutineLock waitCoroutineLock)
{
LockId = lockId;
WaitCoroutineLock = waitCoroutineLock;
}
}
internal sealed class OnCoroutineLockTimeout : EventSystem<CoroutineLockTimeout>
{
protected override void Handler(CoroutineLockTimeout self)
{
var selfWaitCoroutineLock = self.WaitCoroutineLock;
if (self.LockId != selfWaitCoroutineLock.LockId)
{
return;
}
Log.Error($"coroutine lock timeout CoroutineLockQueueType:{selfWaitCoroutineLock.CoroutineLock.CoroutineLockType} Key:{selfWaitCoroutineLock.CoroutineLockQueueKey} Tag:{selfWaitCoroutineLock.Tag}");
}
}
/// <summary>
/// 一个协程锁的实例,用户可以用过这个手动释放锁
/// </summary>
public sealed class WaitCoroutineLock : IPool, IDisposable
{
private bool _isPool;
internal string Tag { get; private set; }
internal long LockId { get; private set; }
internal long TimerId { get; private set; }
internal long CoroutineLockQueueKey { get; private set; }
internal CoroutineLock CoroutineLock { get; private set; }
private bool _isSetResult;
private FTask<WaitCoroutineLock> _tcs;
private WaitCoroutineLockPool _waitCoroutineLockPool;
internal void Initialize(CoroutineLock coroutineLock, WaitCoroutineLockPool waitCoroutineLockPool, ref long coroutineLockQueueKey, ref long timerId, ref long lockId, string tag)
{
Tag = tag;
LockId = lockId;
TimerId = timerId;
CoroutineLock = coroutineLock;
CoroutineLockQueueKey = coroutineLockQueueKey;
_waitCoroutineLockPool = waitCoroutineLockPool;
}
/// <summary>
/// 释放协程锁
/// </summary>
public void Dispose()
{
if (LockId == 0)
{
Log.Error("WaitCoroutineLock is already disposed");
return;
}
CoroutineLock.Release(CoroutineLockQueueKey);
_tcs = null;
Tag = null;
LockId = 0;
TimerId = 0;
_isSetResult = false;
CoroutineLockQueueKey = 0;
_waitCoroutineLockPool.Return(this);
CoroutineLock = null;
_waitCoroutineLockPool = null;
}
internal FTask<WaitCoroutineLock> Tcs
{
get { return _tcs ??= FTask<WaitCoroutineLock>.Create(); }
}
internal void SetResult()
{
if (_isSetResult)
{
Log.Error("WaitCoroutineLock is already SetResult");
return;
}
_isSetResult = true;
Tcs.SetResult(this);
}
/// <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,399 @@
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Fantasy.Assembly;
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.Entitas;
using Fantasy.Entitas.Interface;
using Fantasy.Helper;
#pragma warning disable CS8604 // Possible null reference argument.
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Fantasy.Entitas
{
internal sealed class UpdateQueueInfo
{
public bool IsStop;
public readonly Type Type;
public readonly long RunTimeId;
public UpdateQueueInfo(Type type, long runTimeId)
{
Type = type;
IsStop = false;
RunTimeId = runTimeId;
}
}
internal sealed class FrameUpdateQueueInfo
{
public readonly Type Type;
public readonly long RunTimeId;
public FrameUpdateQueueInfo(Type type, long runTimeId)
{
Type = type;
RunTimeId = runTimeId;
}
}
/// <summary>
/// Entity管理组件
/// </summary>
public sealed class EntityComponent : Entity, ISceneUpdate, IAssembly
{
private readonly OneToManyList<long, Type> _assemblyList = new();
private readonly OneToManyList<long, Type> _assemblyHashCodes = new();
private readonly Dictionary<Type, IAwakeSystem> _awakeSystems = new();
private readonly Dictionary<Type, IUpdateSystem> _updateSystems = new();
private readonly Dictionary<Type, IDestroySystem> _destroySystems = new();
private readonly Dictionary<Type, IDeserializeSystem> _deserializeSystems = new();
private readonly Dictionary<Type, IFrameUpdateSystem> _frameUpdateSystem = new();
private readonly Dictionary<Type, long> _hashCodes = new Dictionary<Type, long>();
private readonly Queue<UpdateQueueInfo> _updateQueue = new Queue<UpdateQueueInfo>();
private readonly Queue<FrameUpdateQueueInfo> _frameUpdateQueue = new Queue<FrameUpdateQueueInfo>();
private readonly Dictionary<long, UpdateQueueInfo> _updateQueueDic = new Dictionary<long, UpdateQueueInfo>();
internal async FTask<EntityComponent> Initialize()
{
await AssemblySystem.Register(this);
return this;
}
#region Assembly
public FTask Load(long assemblyIdentity)
{
var task = FTask.Create(false);
Scene.ThreadSynchronizationContext.Post(() =>
{
LoadInner(assemblyIdentity);
task.SetResult();
});
return task;
}
public FTask ReLoad(long assemblyIdentity)
{
var task = FTask.Create(false);
Scene.ThreadSynchronizationContext.Post(() =>
{
OnUnLoadInner(assemblyIdentity);
LoadInner(assemblyIdentity);
task.SetResult();
});
return task;
}
public FTask OnUnLoad(long assemblyIdentity)
{
var task = FTask.Create(false);
Scene.ThreadSynchronizationContext.Post(() =>
{
OnUnLoadInner(assemblyIdentity);
task.SetResult();
});
return task;
}
private void LoadInner(long assemblyIdentity)
{
foreach (var entityType in AssemblySystem.ForEach(assemblyIdentity, typeof(IEntity)))
{
_hashCodes.Add(entityType, HashCodeHelper.ComputeHash64(entityType.FullName));
_assemblyHashCodes.Add(assemblyIdentity, entityType);
}
foreach (var entitiesSystemType in AssemblySystem.ForEach(assemblyIdentity, typeof(IEntitiesSystem)))
{
Type entitiesType = null;
var entity = Activator.CreateInstance(entitiesSystemType);
switch (entity)
{
case IAwakeSystem iAwakeSystem:
{
entitiesType = iAwakeSystem.EntitiesType();
_awakeSystems.Add(entitiesType, iAwakeSystem);
break;
}
case IDestroySystem iDestroySystem:
{
entitiesType = iDestroySystem.EntitiesType();
_destroySystems.Add(entitiesType, iDestroySystem);
break;
}
case IDeserializeSystem iDeserializeSystem:
{
entitiesType = iDeserializeSystem.EntitiesType();
_deserializeSystems.Add(entitiesType, iDeserializeSystem);
break;
}
case IUpdateSystem iUpdateSystem:
{
entitiesType = iUpdateSystem.EntitiesType();
_updateSystems.Add(entitiesType, iUpdateSystem);
break;
}
case IFrameUpdateSystem iFrameUpdateSystem:
{
entitiesType = iFrameUpdateSystem.EntitiesType();
_frameUpdateSystem.Add(entitiesType, iFrameUpdateSystem);
break;
}
default:
{
Log.Error($"IEntitiesSystem not support type {entitiesSystemType}");
return;
}
}
_assemblyList.Add(assemblyIdentity, entitiesType);
}
}
private void OnUnLoadInner(long assemblyIdentity)
{
if (_assemblyHashCodes.TryGetValue(assemblyIdentity, out var entityType))
{
foreach (var type in entityType)
{
_hashCodes.Remove(type);
}
_assemblyHashCodes.RemoveByKey(assemblyIdentity);
}
if (_assemblyList.TryGetValue(assemblyIdentity, out var assembly))
{
foreach (var type in assembly)
{
_awakeSystems.Remove(type);
_updateSystems.Remove(type);
_destroySystems.Remove(type);
_deserializeSystems.Remove(type);
_frameUpdateSystem.Remove(type);
}
_assemblyList.RemoveByKey(assemblyIdentity);
}
}
#endregion
#region Event
/// <summary>
/// 触发实体的唤醒方法
/// </summary>
/// <param name="entity">实体对象</param>
public void Awake(Entity entity)
{
if (!_awakeSystems.TryGetValue(entity.Type, out var awakeSystem))
{
return;
}
try
{
awakeSystem.Invoke(entity);
}
catch (Exception e)
{
Log.Error($"{entity.Type.FullName} Error {e}");
}
}
/// <summary>
/// 触发实体的销毁方法
/// </summary>
/// <param name="entity">实体对象</param>
public void Destroy(Entity entity)
{
if (!_destroySystems.TryGetValue(entity.Type, out var system))
{
return;
}
try
{
system.Invoke(entity);
}
catch (Exception e)
{
Log.Error($"{entity.Type.FullName} Destroy Error {e}");
}
}
/// <summary>
/// 触发实体的反序列化方法
/// </summary>
/// <param name="entity">实体对象</param>
public void Deserialize(Entity entity)
{
if (!_deserializeSystems.TryGetValue(entity.Type, out var system))
{
return;
}
try
{
system.Invoke(entity);
}
catch (Exception e)
{
Log.Error($"{entity.Type.FullName} Deserialize Error {e}");
}
}
#endregion
#region Update
/// <summary>
/// 将实体加入更新队列,准备进行更新
/// </summary>
/// <param name="entity">实体对象</param>
public void StartUpdate(Entity entity)
{
var type = entity.Type;
var entityRuntimeId = entity.RuntimeId;
if (_updateSystems.ContainsKey(type))
{
var updateQueueInfo = new UpdateQueueInfo(type, entityRuntimeId);
_updateQueue.Enqueue(updateQueueInfo);
_updateQueueDic.Add(entityRuntimeId, updateQueueInfo);
}
if (_frameUpdateSystem.ContainsKey(type))
{
_frameUpdateQueue.Enqueue(new FrameUpdateQueueInfo(type, entityRuntimeId));
}
}
/// <summary>
/// 停止实体进行更新
/// </summary>
/// <param name="entity">实体对象</param>
public void StopUpdate(Entity entity)
{
if (!_updateQueueDic.Remove(entity.RuntimeId, out var updateQueueInfo))
{
return;
}
updateQueueInfo.IsStop = true;
}
/// <summary>
/// 执行实体系统的更新逻辑
/// </summary>
public void Update()
{
var updateQueueCount = _updateQueue.Count;
while (updateQueueCount-- > 0)
{
var updateQueueStruct = _updateQueue.Dequeue();
if (updateQueueStruct.IsStop)
{
continue;
}
if (!_updateSystems.TryGetValue(updateQueueStruct.Type, out var updateSystem))
{
continue;
}
var entity = Scene.GetEntity(updateQueueStruct.RunTimeId);
if (entity == null || entity.IsDisposed)
{
_updateQueueDic.Remove(updateQueueStruct.RunTimeId);
continue;
}
_updateQueue.Enqueue(updateQueueStruct);
try
{
updateSystem.Invoke(entity);
}
catch (Exception e)
{
Log.Error($"{updateQueueStruct.Type.FullName} Update Error {e}");
}
}
}
/// <summary>
/// 执行实体系统的帧更新逻辑
/// </summary>
public void FrameUpdate()
{
var count = _frameUpdateQueue.Count;
while (count-- > 0)
{
var frameUpdateQueueStruct = _frameUpdateQueue.Dequeue();
if (!_frameUpdateSystem.TryGetValue(frameUpdateQueueStruct.Type, out var frameUpdateSystem))
{
continue;
}
var entity = Scene.GetEntity(frameUpdateQueueStruct.RunTimeId);
if (entity == null || entity.IsDisposed)
{
continue;
}
_frameUpdateQueue.Enqueue(frameUpdateQueueStruct);
try
{
frameUpdateSystem.Invoke(entity);
}
catch (Exception e)
{
Log.Error($"{frameUpdateQueueStruct.Type.FullName} FrameUpdate Error {e}");
}
}
}
#endregion
public long GetHashCode(Type type)
{
return _hashCodes[type];
}
/// <summary>
/// 释放实体系统管理器资源
/// </summary>
public override void Dispose()
{
_updateQueue.Clear();
_frameUpdateQueue.Clear();
_assemblyList.Clear();
_awakeSystems.Clear();
_updateSystems.Clear();
_destroySystems.Clear();
_deserializeSystems.Clear();
_frameUpdateSystem.Clear();
AssemblySystem.UnRegister(this);
base.Dispose();
}
}
}

View File

@@ -0,0 +1,252 @@
using System;
using System.Reflection;
using Fantasy.Assembly;
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.Entitas;
// ReSharper disable PossibleMultipleEnumeration
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
// ReSharper disable MethodOverloadWithOptionalParameter
namespace Fantasy.Event
{
internal sealed class EventCache
{
public readonly Type EnventType;
public readonly object Obj;
public EventCache(Type enventType, object obj)
{
EnventType = enventType;
Obj = obj;
}
}
public sealed class EventComponent : Entity, IAssembly
{
private readonly OneToManyList<Type, IEvent> _events = new();
private readonly OneToManyList<Type, IAsyncEvent> _asyncEvents = new();
private readonly OneToManyList<long, EventCache> _assemblyEvents = new();
private readonly OneToManyList<long, EventCache> _assemblyAsyncEvents = new();
internal async FTask<EventComponent> Initialize()
{
await AssemblySystem.Register(this);
return this;
}
#region Assembly
public async FTask Load(long assemblyIdentity)
{
var tcs = FTask.Create(false);
Scene.ThreadSynchronizationContext.Post(() =>
{
LoadInner(assemblyIdentity);
tcs.SetResult();
});
await tcs;
}
public async FTask ReLoad(long assemblyIdentity)
{
var tcs = FTask.Create(false);
Scene.ThreadSynchronizationContext.Post(() =>
{
OnUnLoadInner(assemblyIdentity);
LoadInner(assemblyIdentity);
tcs.SetResult();
});
await tcs;
}
public async FTask OnUnLoad(long assemblyIdentity)
{
var tcs = FTask.Create(false);
Scene.ThreadSynchronizationContext.Post(() =>
{
OnUnLoadInner(assemblyIdentity);
tcs.SetResult();
});
await tcs;
}
private void LoadInner(long assemblyIdentity)
{
foreach (var type in AssemblySystem.ForEach(assemblyIdentity, typeof(IEvent)))
{
var @event = (IEvent)Activator.CreateInstance(type);
if (@event == null)
{
continue;
}
var eventType = @event.EventType();
_events.Add(eventType, @event);
_assemblyEvents.Add(assemblyIdentity, new EventCache(eventType, @event));
}
foreach (var type in AssemblySystem.ForEach(assemblyIdentity, typeof(IAsyncEvent)))
{
var @event = (IAsyncEvent)Activator.CreateInstance(type);
if (@event == null)
{
continue;
}
var eventType = @event.EventType();
_asyncEvents.Add(eventType, @event);
_assemblyAsyncEvents.Add(assemblyIdentity, new EventCache(eventType, @event));
}
}
private void OnUnLoadInner(long assemblyIdentity)
{
if (_assemblyEvents.TryGetValue(assemblyIdentity, out var events))
{
foreach (var @event in events)
{
_events.RemoveValue(@event.EnventType, (IEvent)@event.Obj);
}
_assemblyEvents.RemoveByKey(assemblyIdentity);
}
if (_assemblyAsyncEvents.TryGetValue(assemblyIdentity, out var asyncEvents))
{
foreach (var @event in asyncEvents)
{
_asyncEvents.RemoveValue(@event.EnventType, (IAsyncEvent)@event.Obj);
}
_assemblyAsyncEvents.RemoveByKey(assemblyIdentity);
}
}
#endregion
#region Publish
/// <summary>
/// 发布一个值类型的事件数据。
/// </summary>
/// <typeparam name="TEventData">事件数据类型(值类型)。</typeparam>
/// <param name="eventData">事件数据实例。</param>
public void Publish<TEventData>(TEventData eventData) where TEventData : struct
{
if (!_events.TryGetValue(typeof(TEventData), out var list))
{
return;
}
foreach (var @event in list)
{
try
{
@event.Invoke(eventData);
}
catch (Exception e)
{
Log.Error(e);
}
}
}
/// <summary>
/// 发布一个继承自 Entity 的事件数据。
/// </summary>
/// <typeparam name="TEventData">事件数据类型(继承自 Entity。</typeparam>
/// <param name="eventData">事件数据实例。</param>
/// <param name="isDisposed">是否释放事件数据。</param>
public void Publish<TEventData>(TEventData eventData, bool isDisposed = true) where TEventData : Entity
{
if (!_events.TryGetValue(typeof(TEventData), out var list))
{
return;
}
foreach (var @event in list)
{
try
{
@event.Invoke(eventData);
}
catch (Exception e)
{
Log.Error(e);
}
}
if (isDisposed)
{
eventData.Dispose();
}
}
/// <summary>
/// 异步发布一个值类型的事件数据。
/// </summary>
/// <typeparam name="TEventData">事件数据类型(值类型)。</typeparam>
/// <param name="eventData">事件数据实例。</param>
/// <returns>表示异步操作的任务。</returns>
public async FTask PublishAsync<TEventData>(TEventData eventData) where TEventData : struct
{
if (!_asyncEvents.TryGetValue(typeof(TEventData), out var list))
{
return;
}
using var tasks = ListPool<FTask>.Create();
foreach (var @event in list)
{
tasks.Add(@event.InvokeAsync(eventData));
}
await FTask.WaitAll(tasks);
}
/// <summary>
/// 异步发布一个继承自 Entity 的事件数据。
/// </summary>
/// <typeparam name="TEventData">事件数据类型(继承自 Entity。</typeparam>
/// <param name="eventData">事件数据实例。</param>
/// <param name="isDisposed">是否释放事件数据。</param>
/// <returns>表示异步操作的任务。</returns>
public async FTask PublishAsync<TEventData>(TEventData eventData, bool isDisposed = true) where TEventData : Entity
{
if (!_asyncEvents.TryGetValue(eventData.GetType(), out var list))
{
return;
}
using var tasks = ListPool<FTask>.Create();
foreach (var @event in list)
{
tasks.Add(@event.InvokeAsync(eventData));
}
await FTask.WaitAll(tasks);
if (isDisposed)
{
eventData.Dispose();
}
}
#endregion
public override void Dispose()
{
_events.Clear();
_asyncEvents.Clear();
_assemblyEvents.Clear();
_assemblyAsyncEvents.Clear();
base.Dispose();
}
}
}

View File

@@ -0,0 +1,112 @@
using System;
using Fantasy.Async;
namespace Fantasy.Event
{
/// <summary>
/// 事件的接口
/// </summary>
public interface IEvent
{
/// <summary>
/// 用于指定事件的Type
/// </summary>
/// <returns></returns>
Type EventType();
/// <summary>
/// 时间内部使用的入口
/// </summary>
/// <param name="self"></param>
void Invoke(object self);
}
/// <summary>
/// 异步事件的接口
/// </summary>
public interface IAsyncEvent
{
/// <summary>
/// <see cref="IEvent.EventType"/>
/// </summary>
/// <returns></returns>
Type EventType();
/// <summary>
/// <see cref="IEvent.Invoke"/>
/// </summary>
/// <returns></returns>
FTask InvokeAsync(object self);
}
/// <summary>
/// 事件的抽象类,要使用事件必须要继承这个抽象接口。
/// </summary>
/// <typeparam name="T">要监听的事件泛型类型</typeparam>
public abstract class EventSystem<T> : IEvent
{
private readonly Type _selfType = typeof(T);
/// <summary>
/// <see cref="IEvent.EventType"/>
/// </summary>
/// <returns></returns>
public Type EventType()
{
return _selfType;
}
/// <summary>
/// 事件调用的方法,要在这个方法里编写事件发生的逻辑
/// </summary>
/// <param name="self"></param>
protected abstract void Handler(T self);
/// <summary>
/// <see cref="IEvent.Invoke"/>
/// </summary>
/// <returns></returns>
public void Invoke(object self)
{
try
{
Handler((T) self);
}
catch (Exception e)
{
Log.Error($"{_selfType.Name} Error {e}");
}
}
}
/// <summary>
/// 异步事件的抽象类,要使用事件必须要继承这个抽象接口。
/// </summary>
/// <typeparam name="T">要监听的事件泛型类型</typeparam>
public abstract class AsyncEventSystem<T> : IAsyncEvent
{
private readonly Type _selfType = typeof(T);
/// <summary>
/// <see cref="IEvent.EventType"/>
/// </summary>
/// <returns></returns>
public Type EventType()
{
return _selfType;
}
/// <summary>
/// 事件调用的方法,要在这个方法里编写事件发生的逻辑
/// </summary>
/// <param name="self"></param>
protected abstract FTask Handler(T self);
/// <summary>
/// <see cref="IEvent.Invoke"/>
/// </summary>
/// <returns></returns>
public async FTask InvokeAsync(object self)
{
try
{
await Handler((T) self);
}
catch (Exception e)
{
Log.Error($"{_selfType.Name} Error {e}");
}
}
}
}

View File

@@ -0,0 +1,139 @@
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Fantasy.DataStructure.Collection;
using Fantasy.Entitas;
using Fantasy.Pool;
using Fantasy.Serialize;
namespace Fantasy.Entitas
{
/// <summary>
/// 消息的对象池组件
/// </summary>
public sealed class MessagePoolComponent : Entity
{
private int _poolCount;
private const int MaxCapacity = ushort.MaxValue;
private readonly OneToManyQueue<Type, AMessage> _poolQueue = new OneToManyQueue<Type, AMessage>();
private readonly Dictionary<Type, Func<AMessage>> _typeCheckCache = new Dictionary<Type, Func<AMessage>>();
/// <summary>
/// 销毁组件
/// </summary>
public override void Dispose()
{
_poolCount = 0;
_poolQueue.Clear();
_typeCheckCache.Clear();
base.Dispose();
}
/// <summary>
/// 从对象池里获取一个消息,如果没有就创建一个新的
/// </summary>
/// <typeparam name="T">消息的泛型类型</typeparam>
/// <returns></returns>
public T Rent<T>() where T : AMessage, new()
{
if (!_poolQueue.TryDequeue(typeof(T), out var queue))
{
var instance = new T();
instance.SetScene(Scene);
instance.SetIsPool(true);
return instance;
}
queue.SetIsPool(true);
_poolCount--;
return (T)queue;
}
/// <summary>
/// <see cref="Rent"/>
/// </summary>
/// <param name="type">消息的类型</param>
/// <returns></returns>
/// <exception cref="NotSupportedException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public AMessage Rent(Type type)
{
if (!_poolQueue.TryDequeue(type, out var queue))
{
if (!_typeCheckCache.TryGetValue(type, out var createInstance))
{
if (!typeof(AMessage).IsAssignableFrom(type))
{
throw new NotSupportedException($"{this.GetType().FullName} Type:{type.FullName} must inherit from IPool");
}
else
{
createInstance = CreateInstance.CreateMessage(type);
_typeCheckCache[type] = createInstance;
}
}
var instance = createInstance();
instance.SetScene(Scene);
instance.SetIsPool(true);
return instance;
}
queue.SetIsPool(true);
_poolCount--;
return queue;
}
/// <summary>
/// 返还一个消息到对象池中
/// </summary>
/// <param name="obj"></param>
public void Return(AMessage obj)
{
if (obj == null)
{
return;
}
if (!obj.IsPool())
{
return;
}
if (_poolCount >= MaxCapacity)
{
return;
}
_poolCount++;
obj.SetIsPool(false);
_poolQueue.Enqueue(obj.GetType(), obj);
}
/// <summary>
/// <see cref="Return"/>
/// </summary>
/// <param name="obj">返还的消息</param>
/// <typeparam name="T">返还的消息泛型类型</typeparam>
public void Return<T>(T obj) where T : AMessage
{
if (obj == null)
{
return;
}
if (!obj.IsPool())
{
return;
}
if (_poolCount >= MaxCapacity)
{
return;
}
_poolCount++;
obj.SetIsPool(false);
_poolQueue.Enqueue(typeof(T), obj);
}
}
}

View File

@@ -0,0 +1,167 @@
// ReSharper disable SuspiciousTypeConversion.Global
using Fantasy.Assembly;
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.Entitas;
using Fantasy.Entitas.Interface;
using Fantasy.Helper;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable CS8604 // Possible null reference argument.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
#if FANTASY_NET
namespace Fantasy.SingleCollection
{
/// <summary>
/// 用于处理Entity下的实体进行数据库分表存储的组件
/// </summary>
public sealed class SingleCollectionComponent : Entity, IAssembly
{
private CoroutineLock _coroutineLock;
private readonly OneToManyHashSet<Type, string> _collection = new OneToManyHashSet<Type, string>();
private readonly OneToManyList<long, SingleCollectionInfo> _assemblyCollections =
new OneToManyList<long, SingleCollectionInfo>();
private sealed class SingleCollectionInfo(Type rootType, string collectionName)
{
public readonly Type RootType = rootType;
public readonly string CollectionName = collectionName;
}
internal async FTask<SingleCollectionComponent> Initialize()
{
var coroutineLockType = HashCodeHelper.ComputeHash64(GetType().FullName);
_coroutineLock = Scene.CoroutineLockComponent.Create(coroutineLockType);
await AssemblySystem.Register(this);
return this;
}
#region Assembly
public async FTask Load(long assemblyIdentity)
{
var tcs = FTask.Create(false);
Scene.ThreadSynchronizationContext.Post(() =>
{
LoadInner(assemblyIdentity);
tcs.SetResult();
});
await tcs;
}
public async FTask ReLoad(long assemblyIdentity)
{
var tcs = FTask.Create(false);
Scene.ThreadSynchronizationContext.Post(() =>
{
OnUnLoadInner(assemblyIdentity);
LoadInner(assemblyIdentity);
tcs.SetResult();
});
await tcs;
}
public async FTask OnUnLoad(long assemblyIdentity)
{
var tcs = FTask.Create(false);
Scene.ThreadSynchronizationContext.Post(() =>
{
OnUnLoadInner(assemblyIdentity);
tcs.SetResult();
});
await tcs;
}
private void LoadInner(long assemblyIdentity)
{
foreach (var type in AssemblySystem.ForEach(assemblyIdentity, typeof(ISupportedSingleCollection)))
{
var customAttributes = type.GetCustomAttributes(typeof(SingleCollectionAttribute), false);
if (customAttributes.Length == 0)
{
Log.Error(
$"type {type.FullName} Implemented the interface of ISingleCollection, requiring the implementation of SingleCollectionAttribute");
continue;
}
var singleCollectionAttribute = (SingleCollectionAttribute)customAttributes[0];
var rootType = singleCollectionAttribute.RootType;
var collectionName = singleCollectionAttribute.CollectionName;
_collection.Add(rootType, collectionName);
_assemblyCollections.Add(assemblyIdentity, new SingleCollectionInfo(rootType, collectionName));
}
}
private void OnUnLoadInner(long assemblyIdentity)
{
if (!_assemblyCollections.TryGetValue(assemblyIdentity, out var types))
{
return;
}
foreach (var singleCollectionInfo in types)
{
_collection.RemoveValue(singleCollectionInfo.RootType, singleCollectionInfo.CollectionName);
}
_assemblyCollections.RemoveByKey(assemblyIdentity);
}
#endregion
#region Collections
/// <summary>
/// 通过数据库获取某一个实体类型下所有的分表数据到当前实体下,并且会自动建立父子关系。
/// </summary>
/// <param name="entity">实体实例</param>
/// <typeparam name="T">实体泛型类型</typeparam>
public async FTask GetCollections<T>(T entity) where T : Entity, ISingleCollectionRoot
{
if (!_collection.TryGetValue(typeof(T), out var collections))
{
return;
}
var worldDateBase = Scene.World.DataBase;
using (await _coroutineLock.Wait(entity.Id))
{
foreach (var collectionName in collections)
{
var singleCollection = await worldDateBase.QueryNotLock<Entity>(entity.Id, true, collectionName);
entity.AddComponent(singleCollection);
}
}
}
/// <summary>
/// 存储当前实体下支持分表的组件到数据中,包括存储实体本身。
/// </summary>
/// <param name="entity">实体实例</param>
/// <typeparam name="T">实体泛型类型</typeparam>
public async FTask SaveCollections<T>(T entity) where T : Entity, ISingleCollectionRoot
{
using var collections = ListPool<Entity>.Create();
foreach (var treeEntity in entity.ForEachSingleCollection)
{
if (treeEntity is not ISupportedSingleCollection)
{
continue;
}
collections.Add(treeEntity);
}
collections.Add(entity);
await entity.Scene.World.DataBase.Save(entity.Id, collections);
}
#endregion
}
}
#endif

View File

@@ -0,0 +1,10 @@
using Fantasy.Event;
namespace Fantasy.Timer
{
/// <summary>
/// 计时器抽象类,提供了一个基础框架,用于创建处理计时器事件的具体类。
/// </summary>
/// <typeparam name="T">事件的类型参数</typeparam>
public abstract class TimerHandler<T> : EventSystem<T> { }
}

View File

@@ -0,0 +1,49 @@
// #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
// #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
// namespace Fantasy
// {
// public sealed class ScheduledTaskPool : PoolCore<ScheduledTask>
// {
// public ScheduledTaskPool() : base(2000) { }
//
// public ScheduledTask Rent(Action action, ref int rounds, ref int finalSlot)
// {
// var scheduledTask = Rent();
// scheduledTask.Rounds = rounds;
// scheduledTask.Action = action;
// scheduledTask.FinalSlot = finalSlot;
// return scheduledTask;
// }
//
// public override void Return(ScheduledTask item)
// {
// base.Return(item);
// item.Dispose();
// }
// }
//
// public sealed class ScheduledTask : IPool, IDisposable
// {
// public int Rounds;
// public int FinalSlot;
// public Action Action;
// public LinkedListNode<ScheduledTask> Node;
//
// public bool IsPool { get; set; }
// public ScheduledTask() { }
// public ScheduledTask(Action action, ref int rounds, ref int finalSlot)
// {
// Action = action;
// Rounds = rounds;
// FinalSlot = finalSlot;
// }
//
// public void Dispose()
// {
// Rounds = 0;
// FinalSlot = 0;
// Action = null;
// Node = null;
// }
// }
// }

View File

@@ -0,0 +1,134 @@
// using System.Runtime.CompilerServices;
// // ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
// #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
//
// namespace Fantasy
// {
// public sealed class TimeWheel
// {
// private int _currentIndex;
// private ScheduledTaskPool _scheduledTaskPool;
//
// private readonly Scene _scene;
// private readonly int _wheelSize;
// private readonly int _tickDuration;
// private readonly TimeWheel _upperLevelWheel;
// private readonly LinkedList<ScheduledTask>[] _wheel;
// private readonly Queue<ScheduledTask> _tasksToReschedule = new Queue<ScheduledTask>();
// private readonly Dictionary<long, ScheduledTask> _taskDictionary = new Dictionary<long, ScheduledTask>();
//
// public TimeWheel(TimerComponent timerComponent, int wheelSize, int tickDuration, TimeWheel upperLevelWheel = null)
// {
// _scene = timerComponent.Scene;
// _wheelSize = wheelSize;
// _tickDuration = tickDuration;
// _upperLevelWheel = upperLevelWheel;
// _scheduledTaskPool = timerComponent.ScheduledTaskPool;
// _wheel = new LinkedList<ScheduledTask>[_wheelSize];
// for (var i = 0; i < wheelSize; i++)
// {
// _wheel[i] = new LinkedList<ScheduledTask>();
// }
// }
//
// public long Schedule(Action action, int delay)
// {
// var ticks = delay / _tickDuration;
// var futureIndex = ticks + _currentIndex;
// var rounds = futureIndex / _wheelSize;
// var slot = futureIndex % _wheelSize;
//
// if (slot == 0)
// {
// slot = _wheelSize - 1;
// rounds--;
// }
// else
// {
// slot--;
// }
//
// var taskId = _scene.RuntimeIdFactory.Create;
// var task = _scheduledTaskPool.Rent(action, ref rounds, ref slot);
// task.Node = _wheel[slot].AddLast(task);
// _taskDictionary.Add(taskId, task);
// Console.WriteLine($"Schedule rounds:{rounds} slot:{slot} _currentIndex:{_currentIndex}");
// return taskId;
// }
//
// public bool Remove(int taskId)
// {
// if (!_taskDictionary.TryGetValue(taskId, out var task))
// {
// return false;
// }
//
// _taskDictionary.Remove(taskId);
// _wheel[task.FinalSlot].Remove(task.Node);
// _scheduledTaskPool.Return(task);
// Console.WriteLine("找到已经删除了任务");
// return true;
// }
//
// public void Tick(object? state)
// {
// var currentWheel = _wheel[_currentIndex];
//
// if (currentWheel.Count == 0)
// {
// AdvanceIndex();
// return;
// }
//
// var currentNode = currentWheel.First;
//
// while (currentNode != null)
// {
// var nextNode = currentNode.Next;
// var task = currentNode.Value;
//
// if (task.Rounds <= 0 && task.FinalSlot == _currentIndex)
// {
// try
// {
// task.Action.Invoke();
// }
// catch (Exception ex)
// {
// Log.Error($"Exception during task execution: {ex.Message}");
// }
// }
// else
// {
// task.Rounds--;
// _tasksToReschedule.Enqueue(task);
// }
//
// currentWheel.Remove(currentNode);
// currentNode = nextNode;
// }
//
// RescheduleTasks();
// AdvanceIndex();
// }
//
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// private void AdvanceIndex()
// {
// _currentIndex = (_currentIndex + 1) % _wheelSize;
// if (_currentIndex == 0 && _upperLevelWheel != null)
// {
// _upperLevelWheel.Tick(null);
// }
// }
//
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// private void RescheduleTasks()
// {
// while (_tasksToReschedule.TryDequeue(out var task))
// {
// _wheel[task.FinalSlot].AddLast(task);
// }
// }
// }
// }

View File

@@ -0,0 +1,27 @@
using System;
using System.Runtime.InteropServices;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable CS8625
#pragma warning disable CS8618
namespace Fantasy.Timer
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct TimerAction
{
public long TimerId;
public long StartTime;
public long TriggerTime;
public readonly object Callback;
public readonly TimerType TimerType;
public TimerAction(long timerId, TimerType timerType, long startTime, long triggerTime, object callback)
{
TimerId = timerId;
Callback = callback;
TimerType = timerType;
StartTime = startTime;
TriggerTime = triggerTime;
}
}
}

View File

@@ -0,0 +1,52 @@
// ReSharper disable ForCanBeConvertedToForeach
using Fantasy.Entitas;
using Fantasy.Entitas.Interface;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
#if FANTASY_UNITY
using UnityEngine;
#endif
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Fantasy.Timer
{
public sealed class TimerComponentUpdateSystem : UpdateSystem<TimerComponent>
{
protected override void Update(TimerComponent self)
{
self.Update();
}
}
/// <summary>
/// 时间调度组件
/// </summary>
public sealed class TimerComponent : Entity
{
/// <summary>
/// 使用系统时间创建的计时器核心。
/// </summary>
public TimerSchedulerNet Net { get; private set; }
#if FANTASY_UNITY
/// <summary>
/// 使用 Unity 时间创建的计时器核心。
/// </summary>
public TimerSchedulerNetUnity Unity { get; private set; }
#endif
internal TimerComponent Initialize()
{
Net = new TimerSchedulerNet(Scene);
#if FANTASY_UNITY
Unity = new TimerSchedulerNetUnity(Scene);
#endif
return this;
}
public void Update()
{
Net.Update();
#if FANTASY_UNITY
Unity.Update();
#endif
}
}
}

View File

@@ -0,0 +1,390 @@
using System;
using System.Collections.Generic;
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.Helper;
// ReSharper disable UnusedParameter.Global
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
namespace Fantasy.Timer
{
/// <summary>
/// 基于系统事件的任务调度系统
/// </summary>
public sealed class TimerSchedulerNet
{
private readonly Scene _scene;
private long _idGenerator;
private long _minTime; // 最小时间
private readonly Queue<long> _timeOutTime = new Queue<long>();
private readonly Queue<long> _timeOutTimerIds = new Queue<long>();
private readonly Dictionary<long, TimerAction> _timerActions = new Dictionary<long, TimerAction>();
private readonly SortedOneToManyList<long, long> _timeId = new(); // 时间与计时器ID的有序一对多列表
private long GetId => ++_idGenerator;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="scene">当前的Scene</param>
public TimerSchedulerNet(Scene scene)
{
_scene = scene;
}
private long Now()
{
return TimeHelper.Now;
}
/// <summary>
/// 驱动方法,只有调用这个方法任务系统才会正常运转。
/// </summary>
public void Update()
{
if (_timeId.Count == 0)
{
return;
}
var currentTime = Now();
if (currentTime < _minTime)
{
return;
}
// 遍历时间ID列表查找超时的计时器任务
foreach (var (key, _) in _timeId)
{
if (key > currentTime)
{
_minTime = key;
break;
}
_timeOutTime.Enqueue(key);
}
// 处理超时的计时器任务
while (_timeOutTime.TryDequeue(out var time))
{
var timerIds = _timeId[time];
for (var i = 0; i < timerIds.Count; ++i)
{
_timeOutTimerIds.Enqueue(timerIds[i]);
}
_timeId.Remove(time);
// _timeId.RemoveKey(time);
}
if (_timeId.Count == 0)
{
_minTime = long.MaxValue;
}
// 执行超时的计时器任务的回调操作
while (_timeOutTimerIds.TryDequeue(out var timerId))
{
if (!_timerActions.Remove(timerId, out var timerAction))
{
continue;
}
// 根据计时器类型执行不同的操作
switch (timerAction.TimerType)
{
case TimerType.OnceWaitTimer:
{
var tcs = (FTask<bool>)timerAction.Callback;
tcs.SetResult(true);
break;
}
case TimerType.OnceTimer:
{
if (timerAction.Callback is not Action action)
{
Log.Error($"timerAction {timerAction.ToJson()}");
break;
}
action();
break;
}
case TimerType.RepeatedTimer:
{
if (timerAction.Callback is not Action action)
{
Log.Error($"timerAction {timerAction.ToJson()}");
break;
}
timerAction.StartTime = Now();
AddTimer(ref timerAction);
action();
break;
}
}
}
}
private void AddTimer(ref TimerAction timer)
{
var tillTime = timer.StartTime + timer.TriggerTime;
_timeId.Add(tillTime, timer.TimerId);
_timerActions.Add(timer.TimerId, timer);
if (tillTime < _minTime)
{
_minTime = tillTime;
}
}
/// <summary>
/// 异步等待指定时间。
/// </summary>
/// <param name="time">等待的时间长度。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>等待是否成功。</returns>
public async FTask<bool> WaitAsync(long time, FCancellationToken cancellationToken = null)
{
if (time <= 0)
{
return true;
}
var now = Now();
var timerId = GetId;
var tcs = FTask<bool>.Create();
var timerAction = new TimerAction(timerId, TimerType.OnceWaitTimer, now, time, tcs);
void CancelActionVoid()
{
if (Remove(timerId))
{
tcs.SetResult(false);
}
}
bool result;
try
{
cancellationToken?.Add(CancelActionVoid);
AddTimer(ref timerAction);
result = await tcs;
}
finally
{
cancellationToken?.Remove(CancelActionVoid);
}
return result;
}
/// <summary>
/// 异步等待直到指定时间。
/// </summary>
/// <param name="tillTime">等待的目标时间。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>等待是否成功。</returns>
public async FTask<bool> WaitTillAsync(long tillTime, FCancellationToken cancellationToken = null)
{
var now = Now();
if (now >= tillTime)
{
return true;
}
var timerId = GetId;
var tcs = FTask<bool>.Create();
var timerAction = new TimerAction(timerId, TimerType.OnceWaitTimer, now, tillTime - now, tcs);
void CancelActionVoid()
{
if (Remove(timerId))
{
tcs.SetResult(false);
}
}
bool result;
try
{
cancellationToken?.Add(CancelActionVoid);
AddTimer(ref timerAction);
result = await tcs;
}
finally
{
cancellationToken?.Remove(CancelActionVoid);
}
return result;
}
/// <summary>
/// 异步等待一帧时间。
/// </summary>
/// <returns>等待是否成功。</returns>
public async FTask WaitFrameAsync()
{
#if FANTASY_NET
await WaitAsync(100);
#else
await WaitAsync(1);
#endif
}
/// <summary>
/// 创建一个只执行一次的计时器,直到指定时间
/// </summary>
/// <param name="time">计时器执行的目标时间。</param>
/// <param name="action">计时器回调方法。</param>
/// <returns></returns>
public long OnceTimer(long time, Action action)
{
var now = Now();
var timerId = GetId;
var timerAction = new TimerAction(timerId, TimerType.OnceTimer, now, time, action);
AddTimer(ref timerAction);
return timerId;
}
/// <summary>
/// 创建一个只执行一次的计时器,直到指定时间。
/// </summary>
/// <param name="tillTime">计时器执行的目标时间。</param>
/// <param name="action">计时器回调方法。</param>
/// <returns>计时器的 ID。</returns>
public long OnceTillTimer(long tillTime, Action action)
{
var now = Now();
if (tillTime < now)
{
Log.Error($"new once time too small tillTime:{tillTime} Now:{now}");
}
var timerId = GetId;
var timerAction = new TimerAction(timerId, TimerType.OnceTimer, now, tillTime - now, action);
AddTimer(ref timerAction);
return timerId;
}
/// <summary>
/// 创建一个只执行一次的计时器,用于发布指定类型的事件。
/// </summary>
/// <typeparam name="T">事件类型。</typeparam>
/// <param name="time">计时器执行的延迟时间。</param>
/// <param name="timerHandlerType">事件处理器类型。</param>
/// <returns>计时器的 ID。</returns>
public long OnceTimer<T>(long time, T timerHandlerType) where T : struct
{
void OnceTimerVoid()
{
_scene.EventComponent.Publish(timerHandlerType);
}
return OnceTimer(time, OnceTimerVoid);
}
/// <summary>
/// 创建一个只执行一次的计时器,直到指定时间,用于发布指定类型的事件。
/// </summary>
/// <typeparam name="T">事件类型。</typeparam>
/// <param name="tillTime">计时器执行的目标时间。</param>
/// <param name="timerHandlerType">事件处理器类型。</param>
/// <returns>计时器的 ID。</returns>
public long OnceTillTimer<T>(long tillTime, T timerHandlerType) where T : struct
{
void OnceTillTimerVoid()
{
_scene.EventComponent.Publish(timerHandlerType);
}
return OnceTillTimer(tillTime, OnceTillTimerVoid);
}
/// <summary>
/// 创建一个帧任务
/// </summary>
/// <param name="action"></param>
/// <returns></returns>
public long FrameTimer(Action action)
{
#if FANTASY_NET
return RepeatedTimerInner(100, action);
#else
return RepeatedTimerInner(0, action);
#endif
}
/// <summary>
/// 创建一个重复执行的计时器。
/// </summary>
/// <param name="time">计时器重复间隔的时间。</param>
/// <param name="action">计时器回调方法。</param>
/// <returns>计时器的 ID。</returns>
public long RepeatedTimer(long time, Action action)
{
if (time < 0)
{
Log.Error($"time too small: {time}");
return 0;
}
return RepeatedTimerInner(time, action);
}
/// <summary>
/// 创建一个重复执行的计时器,用于发布指定类型的事件。
/// </summary>
/// <typeparam name="T">事件类型。</typeparam>
/// <param name="time">计时器重复间隔的时间。</param>
/// <param name="timerHandlerType">事件处理器类型。</param>
/// <returns>计时器的 ID。</returns>
public long RepeatedTimer<T>(long time, T timerHandlerType) where T : struct
{
void RepeatedTimerVoid()
{
_scene.EventComponent.Publish(timerHandlerType);
}
return RepeatedTimer(time, RepeatedTimerVoid);
}
private long RepeatedTimerInner(long time, Action action)
{
var now = Now();
var timerId = GetId;
var timerAction = new TimerAction(timerId, TimerType.RepeatedTimer, now, time, action);
AddTimer(ref timerAction);
return timerId;
}
/// <summary>
/// 移除指定 ID 的计时器。
/// </summary>
/// <param name="timerId"></param>
/// <returns></returns>
public bool Remove(ref long timerId)
{
var id = timerId;
timerId = 0;
return Remove(id);
}
/// <summary>
/// 移除指定 ID 的计时器。
/// </summary>
/// <param name="timerId">计时器的 ID。</param>
public bool Remove(long timerId)
{
return timerId != 0 && _timerActions.Remove(timerId, out _);
}
}
}

View File

@@ -0,0 +1,372 @@
#if FANTASY_UNITY
using System;
using System.Collections.Generic;
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.Helper;
using UnityEngine;
namespace Fantasy.Timer
{
public sealed class TimerSchedulerNetUnity
{
private readonly Scene _scene;
private long _idGenerator;
private long _minTime; // 最小时间
private readonly Queue<long> _timeOutTime = new Queue<long>();
private readonly Queue<long> _timeOutTimerIds = new Queue<long>();
private readonly Dictionary<long, TimerAction> _timerActions = new Dictionary<long, TimerAction>();
private readonly SortedOneToManyList<long, long> _timeId = new(); // 时间与计时器ID的有序一对多列表
private long GetId => ++_idGenerator;
public TimerSchedulerNetUnity(Scene scene)
{
_scene = scene;
}
private long Now()
{
return (long)(Time.time * 1000);
}
public void Update()
{
if (_timeId.Count == 0)
{
return;
}
var currentTime = Now();
if (currentTime < _minTime)
{
return;
}
// 遍历时间ID列表查找超时的计时器任务
foreach (var (key, _) in _timeId)
{
if (key > currentTime)
{
_minTime = key;
break;
}
_timeOutTime.Enqueue(key);
}
// 处理超时的计时器任务
while (_timeOutTime.TryDequeue(out var time))
{
var timerIds = _timeId[time];
for (var i = 0; i < timerIds.Count; ++i)
{
_timeOutTimerIds.Enqueue(timerIds[i]);
}
_timeId.Remove(time);
// _timeId.RemoveKey(time);
}
if (_timeId.Count == 0)
{
_minTime = long.MaxValue;
}
// 执行超时的计时器任务的回调操作
while (_timeOutTimerIds.TryDequeue(out var timerId))
{
if (!_timerActions.Remove(timerId, out var timerAction))
{
continue;
}
// 根据计时器类型执行不同的操作
switch (timerAction.TimerType)
{
case TimerType.OnceWaitTimer:
{
var tcs = (FTask<bool>)timerAction.Callback;
tcs.SetResult(true);
break;
}
case TimerType.OnceTimer:
{
if (timerAction.Callback is not Action action)
{
Log.Error($"timerAction {timerAction.ToJson()}");
break;
}
action();
break;
}
case TimerType.RepeatedTimer:
{
if (timerAction.Callback is not Action action)
{
Log.Error($"timerAction {timerAction.ToJson()}");
break;
}
timerAction.StartTime = Now();
AddTimer(ref timerAction);
action();
break;
}
}
}
}
private void AddTimer(ref TimerAction timer)
{
var tillTime = timer.StartTime + timer.TriggerTime;
_timeId.Add(tillTime, timer.TimerId);
_timerActions.Add(timer.TimerId, timer);
if (tillTime < _minTime)
{
_minTime = tillTime;
}
}
/// <summary>
/// 异步等待指定时间。
/// </summary>
/// <param name="time">等待的时间长度。</param>
/// <param name="cancellationToken">可选的取消令牌。</param>
/// <returns>等待是否成功。</returns>
public async FTask<bool> WaitAsync(long time, FCancellationToken cancellationToken = null)
{
if (time <= 0)
{
return true;
}
var now = Now();
var timerId = GetId;
var tcs = FTask<bool>.Create();
var timerAction = new TimerAction(timerId, TimerType.OnceWaitTimer, now, time, tcs);
void CancelActionVoid()
{
if (Remove(timerId))
{
tcs.SetResult(false);
}
}
bool result;
try
{
cancellationToken?.Add(CancelActionVoid);
AddTimer(ref timerAction);
result =await tcs;
}
finally
{
cancellationToken?.Remove(CancelActionVoid);
}
return result;
}
/// <summary>
/// 异步等待直到指定时间。
/// </summary>
/// <param name="tillTime">等待的目标时间。</param>
/// <param name="cancellationToken">可选的取消令牌。</param>
/// <returns>等待是否成功。</returns>
public async FTask<bool> WaitTillAsync(long tillTime, FCancellationToken cancellationToken = null)
{
var now = Now();
if (now >= tillTime)
{
return true;
}
var timerId = GetId;
var tcs = FTask<bool>.Create();
var timerAction = new TimerAction(timerId, TimerType.OnceWaitTimer, now, tillTime - now, tcs);
void CancelActionVoid()
{
if (Remove(timerId))
{
tcs.SetResult(false);
}
}
bool result;
try
{
cancellationToken?.Add(CancelActionVoid);
AddTimer(ref timerAction);
result = await tcs;
}
finally
{
cancellationToken?.Remove(CancelActionVoid);
}
return result;
}
/// <summary>
/// 异步等待一帧时间。
/// </summary>
/// <returns>等待是否成功。</returns>
public async FTask WaitFrameAsync(FCancellationToken cancellationToken = null)
{
await WaitAsync(1, cancellationToken);
}
/// <summary>
/// 创建一个只执行一次的计时器,直到指定时间
/// </summary>
/// <param name="time">计时器执行的目标时间。</param>
/// <param name="action">计时器回调方法。</param>
/// <returns></returns>
public long OnceTimer(long time, Action action)
{
var now = Now();
var timerId = GetId;
var timerAction = new TimerAction(timerId, TimerType.OnceTimer, now, time, action);
AddTimer(ref timerAction);
return timerId;
}
/// <summary>
/// 创建一个只执行一次的计时器,直到指定时间。
/// </summary>
/// <param name="tillTime">计时器执行的目标时间。</param>
/// <param name="action">计时器回调方法。</param>
/// <returns>计时器的 ID。</returns>
public long OnceTillTimer(long tillTime, Action action)
{
var now = Now();
if (tillTime < now)
{
Log.Error($"new once time too small tillTime:{tillTime} Now:{now}");
}
var timerId = GetId;
var timerAction = new TimerAction(timerId, TimerType.OnceTimer, now, tillTime - now, action);
AddTimer(ref timerAction);
return timerId;
}
/// <summary>
/// 创建一个只执行一次的计时器,用于发布指定类型的事件。
/// </summary>
/// <typeparam name="T">事件类型。</typeparam>
/// <param name="time">计时器执行的延迟时间。</param>
/// <param name="timerHandlerType">事件处理器类型。</param>
/// <returns>计时器的 ID。</returns>
public long OnceTimer<T>(long time, T timerHandlerType) where T : struct
{
void OnceTimerVoid()
{
_scene.EventComponent.Publish(timerHandlerType);
}
return OnceTimer(time, OnceTimerVoid);
}
/// <summary>
/// 创建一个只执行一次的计时器,直到指定时间,用于发布指定类型的事件。
/// </summary>
/// <typeparam name="T">事件类型。</typeparam>
/// <param name="tillTime">计时器执行的目标时间。</param>
/// <param name="timerHandlerType">事件处理器类型。</param>
/// <returns>计时器的 ID。</returns>
public long OnceTillTimer<T>(long tillTime, T timerHandlerType) where T : struct
{
void OnceTillTimerVoid()
{
_scene.EventComponent.Publish(timerHandlerType);
}
return OnceTillTimer(tillTime, OnceTillTimerVoid);
}
/// <summary>
/// 创建一个帧任务
/// </summary>
/// <param name="action"></param>
/// <returns></returns>
public long FrameTimer(Action action)
{
return RepeatedTimerInner(1, action);
}
/// <summary>
/// 创建一个重复执行的计时器。
/// </summary>
/// <param name="time">计时器重复间隔的时间。</param>
/// <param name="action">计时器回调方法。</param>
/// <returns>计时器的 ID。</returns>
public long RepeatedTimer(long time, Action action)
{
if (time < 0)
{
Log.Error($"time too small: {time}");
return 0;
}
return RepeatedTimerInner(time, action);
}
/// <summary>
/// 创建一个重复执行的计时器,用于发布指定类型的事件。
/// </summary>
/// <typeparam name="T">事件类型。</typeparam>
/// <param name="time">计时器重复间隔的时间。</param>
/// <param name="timerHandlerType">事件处理器类型。</param>
/// <returns>计时器的 ID。</returns>
public long RepeatedTimer<T>(long time, T timerHandlerType) where T : struct
{
void RepeatedTimerVoid()
{
_scene.EventComponent.Publish(timerHandlerType);
}
return RepeatedTimer(time, RepeatedTimerVoid);
}
private long RepeatedTimerInner(long time, Action action)
{
var now = Now();
var timerId = GetId;
var timerAction = new TimerAction(timerId, TimerType.RepeatedTimer, now, time, action);
AddTimer(ref timerAction);
return timerId;
}
/// <summary>
/// 移除指定 ID 的计时器。
/// </summary>
/// <param name="timerId"></param>
/// <returns></returns>
public bool Remove(ref long timerId)
{
var id = timerId;
timerId = 0;
return Remove(id);
}
/// <summary>
/// 移除指定 ID 的计时器。
/// </summary>
/// <param name="timerId">计时器的 ID。</param>
public bool Remove(long timerId)
{
return timerId != 0 && _timerActions.Remove(timerId, out _);
}
}
}
#endif

View File

@@ -0,0 +1,25 @@
namespace Fantasy.Timer
{
/// <summary>
/// 枚举对象TimerType
/// </summary>
public enum TimerType
{
/// <summary>
/// None
/// </summary>
None,
/// <summary>
/// 一次等待定时器
/// </summary>
OnceWaitTimer,
/// <summary>
/// 一次性定时器
/// </summary>
OnceTimer,
/// <summary>
/// 重复定时器
/// </summary>
RepeatedTimer
}
}

View File

@@ -0,0 +1,66 @@
using System.Collections.Generic;
using Fantasy.Pool;
#pragma warning disable CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint.
namespace Fantasy.Entitas
{
internal sealed class EntityPool : PoolCore
{
public EntityPool() : base(4096) { }
}
internal sealed class EntityList<T> : List<T>, IPool where T : Entity
{
private bool _isPool;
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
internal sealed class EntityListPool<T> : PoolCore<EntityList<T>> where T : Entity
{
public EntityListPool() : base(4096) { }
}
internal sealed class EntitySortedDictionary<TM, TN> : SortedDictionary<TM, TN>, IPool where TN : Entity
{
private bool _isPool;
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
internal sealed class EntitySortedDictionaryPool<TM, TN> : PoolCore<EntitySortedDictionary<TM, TN>> where TN : Entity
{
public EntitySortedDictionaryPool() : base(4096) { }
}
}

View File

@@ -0,0 +1,59 @@
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#pragma warning disable CS8603 // Possible null reference return.
namespace Fantasy.Entitas
{
/// <summary>
/// 实体引用检查组件
/// </summary>
/// <typeparam name="T"></typeparam>
public struct EntityReference<T> where T : Entity
{
private T _entity;
private readonly long _runTimeId;
private EntityReference(T t)
{
if (t == null)
{
_entity = null;
_runTimeId = 0;
return;
}
_entity = t;
_runTimeId = t.RuntimeId;
}
/// <summary>
/// 将一个实体转换为EntityReference
/// </summary>
/// <param name="t">实体泛型类型</param>
/// <returns>返回一个EntityReference</returns>
public static implicit operator EntityReference<T>(T t)
{
return new EntityReference<T>(t);
}
/// <summary>
/// 将一个EntityReference转换为实体
/// </summary>
/// <param name="v">实体泛型类型</param>
/// <returns>当实体已经被销毁过会返回null</returns>
public static implicit operator T(EntityReference<T> v)
{
if (v._entity == null)
{
return null;
}
if (v._entity.RuntimeId != v._runTimeId)
{
v._entity = null;
}
return v._entity;
}
}
}

View File

@@ -0,0 +1,17 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Fantasy.Entitas.Interface
{
/// <summary>
/// Entity保存到数据库的时候会根据子组件设置分离存储特性分表存储在不同的集合表中
/// </summary>
public interface ISingleCollectionRoot { }
public static class SingleCollectionRootChecker<T> where T : Entity
{
public static bool IsSupported { get; }
static SingleCollectionRootChecker()
{
IsSupported = typeof(ISingleCollectionRoot).IsAssignableFrom(typeof(T));
}
}
}

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