Normalize all existing files to LF

This commit is contained in:
Youmu Liu
2026-03-06 18:31:11 +08:00
parent 0a0af53af1
commit 2149bbeede
323 changed files with 113191 additions and 113191 deletions
+6 -6
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.IntelliRush.Options.IntelliRushOptions">
<Options Language="Neutral">
<Enabled>True</Enabled>
<DisabledLanguages />
</Options>
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.IntelliRush.Options.IntelliRushOptions">
<Options Language="Neutral">
<Enabled>True</Enabled>
<DisabledLanguages />
</Options>
</Root>
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.VisualizePlugins.RightMarginLine.Options.RightMarginLineOptions">
<Options Language="Neutral">
<Enabled>True</Enabled>
<Style>Dot</Style>
<Width>2</Width>
</Options>
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.VisualizePlugins.RightMarginLine.Options.RightMarginLineOptions">
<Options Language="Neutral">
<Enabled>True</Enabled>
<Style>Dot</Style>
<Width>2</Width>
</Options>
</Root>
+5 -5
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.VSFeatures.SpellChecker.Options.SpellCheckerOptions">
<Options Language="Neutral">
<SupportedElements>XmlComments|Comments|Strings|VerbatimStrings|PublicIdentifiers|InternalIdentifiers|ProtectedInternalIdentifiers|ProtectedIdentifiers|PrivateIdentifiers|LocalIdentifiers|FileNames</SupportedElements>
</Options>
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.VSFeatures.SpellChecker.Options.SpellCheckerOptions">
<Options Language="Neutral">
<SupportedElements>XmlComments|Comments|Strings|VerbatimStrings|PublicIdentifiers|InternalIdentifiers|ProtectedInternalIdentifiers|ProtectedIdentifiers|PrivateIdentifiers|LocalIdentifiers|FileNames</SupportedElements>
</Options>
</Root>
+5 -5
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.Foundation.CodePlaces.Options.FavoritesListContainer">
<Options Language="Neutral">
<Groups />
</Options>
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.Foundation.CodePlaces.Options.FavoritesListContainer">
<Options Language="Neutral">
<Groups />
</Options>
</Root>
+5 -5
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.VisualStudio.Services.Options.BuildEngineOptions">
<Options Language="Neutral">
<Enabled>True</Enabled>
</Options>
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.VisualStudio.Services.Options.BuildEngineOptions">
<Options Language="Neutral">
<Enabled>True</Enabled>
</Options>
</Root>
+57 -57
View File
@@ -1,58 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.Foundation.Shortcuts.ShortcutsOptions">
<Options Language="Neutral">
<RootFolder>
<SubFolders>
<Item Id="{01ea3896-6cc1-4912-824e-88c7c5a82a25}">
<Bindings>
<Item Id="{88959174-9ffa-429b-bef8-aeddb65b9c2a}" />
<Item Id="{975814d0-802e-4143-a979-22d3f975cfc2}" />
<Item Id="{0260c6ac-cfdb-4874-88c5-66bbf9b84e9b}" />
<Item Id="{dd4ceb84-80b3-4517-ac9b-170dcffe646f}" />
<Item Id="{a1f4a77e-b674-41bd-880f-195c3c0c1dfe}" />
<Item Id="{ee0b4600-2d2c-4700-8d4d-578027321f74}" />
<Item Id="{d37c2c40-a7c6-403f-8556-8fb690d4c656}">
<Enabled>False</Enabled>
</Item>
<Item Id="{baf8010d-02d7-4ac9-b644-0c670f796ce4}" />
<Item Id="{676742ae-19ea-4884-840d-2be66d9bb49c}" />
<Item Id="{43d481e0-19e4-405d-b7e3-687fc610050c}">
<Enabled>False</Enabled>
</Item>
<Item Id="{f1a06e4e-3241-438d-84e5-044e3e907492}" />
<Item Id="{3b8cae25-e23f-443c-98ff-83d38711f26e}" />
<Item Id="{491253a6-324c-4a0f-b4e5-aff443b00fb9}" />
<Item Id="{a06ba470-2048-47ac-b7af-93653ce09359}" />
<Item Id="{1562ccb9-7f21-492d-8c71-42d5ff7ecf9a}" />
<Item Id="{34c575e6-55c2-4d7f-a839-8167bfa1a831}" />
<Item Id="{b253a65c-b13a-42bd-8d59-4a790ce19f3a}" />
<Item Id="{011d4fb6-22ba-4d11-8a05-ed753b5dbd8a}" />
<Item Id="{804634ae-7823-4541-a8fe-d7da6c807b41}" />
<Item Id="{eb8a243c-8645-46cf-98ac-e73a6e32cf3e}" />
<Item Id="{5f2e52af-bb7d-46cc-819d-3f2a2860ba8c}" />
<Item Id="{9848d5d7-d513-4f1e-98aa-fcd21b3eb498}" />
</Bindings>
</Item>
<Item Id="{66d31aae-e1f0-43b9-8108-123cb59d73bd}" />
<Item Id="{4fcfb46a-6fd9-4ba4-a790-6447cb1cf0a5}" />
<Item Id="{55d41133-671c-49f1-a934-6c5d59c3f319}">
<SubFolders>
<Item Id="{4bc2f87d-6dc0-41b5-9b0b-251498b765df}">
<Bindings>
<Item Id="{d18a79d1-fff5-4b71-8301-fb8f2397e0ac}" />
<Item Id="{cc238591-6417-4f4c-bca3-08b8ecbb8a1b}">
<Enabled>False</Enabled>
</Item>
</Bindings>
</Item>
<Item Id="{0d070b5c-9bc8-4784-9071-548ab0ef93e9}" />
</SubFolders>
</Item>
<Item Id="{a3f2b3b4-9497-4db5-9f8b-c4b30a2240ac}" />
<Item Id="{318b8900-85af-4c66-b37f-8a86508e7a89}" />
<Item Id="{d33fb6fa-56b3-40a1-b169-71e855b2e3a2}" />
<Item Id="{4aa0656f-7150-440b-8585-509c548e9d7e}" />
</SubFolders>
</RootFolder>
</Options>
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.Foundation.Shortcuts.ShortcutsOptions">
<Options Language="Neutral">
<RootFolder>
<SubFolders>
<Item Id="{01ea3896-6cc1-4912-824e-88c7c5a82a25}">
<Bindings>
<Item Id="{88959174-9ffa-429b-bef8-aeddb65b9c2a}" />
<Item Id="{975814d0-802e-4143-a979-22d3f975cfc2}" />
<Item Id="{0260c6ac-cfdb-4874-88c5-66bbf9b84e9b}" />
<Item Id="{dd4ceb84-80b3-4517-ac9b-170dcffe646f}" />
<Item Id="{a1f4a77e-b674-41bd-880f-195c3c0c1dfe}" />
<Item Id="{ee0b4600-2d2c-4700-8d4d-578027321f74}" />
<Item Id="{d37c2c40-a7c6-403f-8556-8fb690d4c656}">
<Enabled>False</Enabled>
</Item>
<Item Id="{baf8010d-02d7-4ac9-b644-0c670f796ce4}" />
<Item Id="{676742ae-19ea-4884-840d-2be66d9bb49c}" />
<Item Id="{43d481e0-19e4-405d-b7e3-687fc610050c}">
<Enabled>False</Enabled>
</Item>
<Item Id="{f1a06e4e-3241-438d-84e5-044e3e907492}" />
<Item Id="{3b8cae25-e23f-443c-98ff-83d38711f26e}" />
<Item Id="{491253a6-324c-4a0f-b4e5-aff443b00fb9}" />
<Item Id="{a06ba470-2048-47ac-b7af-93653ce09359}" />
<Item Id="{1562ccb9-7f21-492d-8c71-42d5ff7ecf9a}" />
<Item Id="{34c575e6-55c2-4d7f-a839-8167bfa1a831}" />
<Item Id="{b253a65c-b13a-42bd-8d59-4a790ce19f3a}" />
<Item Id="{011d4fb6-22ba-4d11-8a05-ed753b5dbd8a}" />
<Item Id="{804634ae-7823-4541-a8fe-d7da6c807b41}" />
<Item Id="{eb8a243c-8645-46cf-98ac-e73a6e32cf3e}" />
<Item Id="{5f2e52af-bb7d-46cc-819d-3f2a2860ba8c}" />
<Item Id="{9848d5d7-d513-4f1e-98aa-fcd21b3eb498}" />
</Bindings>
</Item>
<Item Id="{66d31aae-e1f0-43b9-8108-123cb59d73bd}" />
<Item Id="{4fcfb46a-6fd9-4ba4-a790-6447cb1cf0a5}" />
<Item Id="{55d41133-671c-49f1-a934-6c5d59c3f319}">
<SubFolders>
<Item Id="{4bc2f87d-6dc0-41b5-9b0b-251498b765df}">
<Bindings>
<Item Id="{d18a79d1-fff5-4b71-8301-fb8f2397e0ac}" />
<Item Id="{cc238591-6417-4f4c-bca3-08b8ecbb8a1b}">
<Enabled>False</Enabled>
</Item>
</Bindings>
</Item>
<Item Id="{0d070b5c-9bc8-4784-9071-548ab0ef93e9}" />
</SubFolders>
</Item>
<Item Id="{a3f2b3b4-9497-4db5-9f8b-c4b30a2240ac}" />
<Item Id="{318b8900-85af-4c66-b37f-8a86508e7a89}" />
<Item Id="{d33fb6fa-56b3-40a1-b169-71e855b2e3a2}" />
<Item Id="{4aa0656f-7150-440b-8585-509c548e9d7e}" />
</SubFolders>
</RootFolder>
</Options>
</Root>
@@ -1,93 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.Foundation.Templates.TemplatesOptions">
<Options Language="C#">
<RootCategory>
<Categories>
<Item Id="{9bc0d39a-b20c-41bb-bd1c-4d08d6d999a9}">
<Templates>
<Item Id="{2b7014a2-6093-4095-ad6d-799b52ef74e0}">
<Items>
<Item Id="{b3ec38a7-ed71-4ab9-9a43-1623fc3d4c30}">
<Expansion>//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="«?FileBase»"
// Project: StgSharp
// AuthorGroup: «?GetUserInfo(Company)»
// Copyright (c) «?GetUserInfo(Company)». All rights reserved.
//
//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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------</Expansion>
</Item>
</Items>
</Item>
<Item Id="{5d7e8673-3c4d-4f8c-98e5-96799d699c6b}" />
<Item Id="{871cff6e-18a7-44e2-9980-cf75ac927e7d}" />
<Item Id="{fc65f206-ebed-4567-b2ad-5da5fe2d5383}" />
<Item Id="{a4ca82b6-2912-47f4-bc3e-fa61fa152c64}" />
<Item Id="{55344716-9e28-4e48-b69f-4ee8c28127cb}" />
<Item Id="{cf975a83-9d8e-420e-a655-27d52cf13c09}" />
<Item Id="{83aac52b-9b01-47e7-932a-8b56e8b86b10}" />
<Item Id="{dc9f70e9-7053-45ed-90a6-e8257db37251}" />
<Item Id="{bfc86380-a990-47cc-9262-d6d644337e35}" />
<Item Id="{2830b620-b907-4390-8eda-c030402a9968}" />
<Item Id="{efc78e7d-fbe7-4a4f-a489-3ff6b881442b}" />
<Item Id="{336f73d0-1064-4ced-99a4-a36400438379}" />
<Item Id="{5babdc02-57e8-4854-82f7-9a9b97e48dad}" />
<Item Id="{8f6f1bb6-a38a-4c81-ae9b-f77a8709a73d}" />
<Item Id="{b0fb5608-05db-47c4-94b4-2bc7f72bc642}" />
<Item Id="{a43c547a-2d44-46eb-b39b-d39f7eb65a24}" />
<Item Id="{890dc8c3-dd34-489e-996a-4f3651f3e72a}" />
<Item Id="{d420e166-39c2-4ea1-a7b2-36416f33de4f}" />
<Item Id="{e4028d8a-d830-4882-b3ac-88e9913d996c}" />
<Item Id="{ed44f8f7-61d8-4932-ac14-6fb30ff55efb}" />
<Item Id="{d7b9b3af-94a6-4c1b-92c8-8058d1d71f0e}" />
<Item Id="{e91be916-8c04-4ebd-a9af-ddc38a046272}" />
<Item Id="{65b10956-7ae0-44ed-a376-6195f7e4d953}" />
</Templates>
</Item>
<Item Id="{8e4132de-c894-486f-8004-e2d08d0ba979}" />
<Item Id="{2433228d-3ae2-4022-aa52-7ab7a1a3116b}" />
<Item Id="{1379a9c1-e7a0-4872-87e5-24ef7c4a1315}" />
<Item Id="{80783537-1b32-4a51-b716-b32036574f4f}" />
<Item Id="{6b1c63bd-043f-4639-b757-a0c6132270fa}" />
<Item Id="{2ff95045-9084-4395-93a8-70d11cc8cdeb}" />
<Item Id="{633d268e-6d35-46c7-ac5d-d2885ca35a0d}" />
<Item Id="{34b6c10a-ee60-4c30-9f5c-e0c033ce7867}" />
<Item Id="{e63d4dbd-49ce-4fd6-ab9d-99e0bfc60b35}" />
<Item Id="{f4703284-a160-4272-be22-3366ade5e43f}" />
<Item Id="{51f7a921-200c-43e3-944c-54521ed0c201}" />
<Item Id="{08077498-5ebe-4300-ad58-bcbf5a3ebac3}" />
<Item Id="{90ee1142-998a-40f3-8aa1-fe850f766551}" />
<Item Id="{662a95d0-4d80-484b-b649-044f1464ff4a}" />
<Item Id="{9c9bbf9d-c82c-450d-bafa-2d8b1f6a4a63}" />
<Item Id="{78fc30d9-aa85-4d78-a3f0-8eeb5a2e1496}" />
<Item Id="{cb08e1a8-1829-4409-87e1-d9a9d3eea8a3}" />
<Item Id="{96418b0c-83d3-4656-90c9-59d4139e045c}" />
<Item Id="{447c50a1-8ec8-44ee-9915-7f0548350093}" />
<Item Id="{a641e756-107f-4e82-9952-010819e35abe}" />
<Item Id="{4cada8e9-90e0-4fe3-8171-91a3735d1447}" />
<Item Id="{3888be66-16ab-4a3e-b416-144246345fa0}" />
<Item Id="{0158dfdd-6429-462c-931f-1a37959d3b42}" />
<Item Id="{bf0539d1-191e-4764-b583-c95277cfa4bf}" />
</Categories>
</RootCategory>
</Options>
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.Foundation.Templates.TemplatesOptions">
<Options Language="C#">
<RootCategory>
<Categories>
<Item Id="{9bc0d39a-b20c-41bb-bd1c-4d08d6d999a9}">
<Templates>
<Item Id="{2b7014a2-6093-4095-ad6d-799b52ef74e0}">
<Items>
<Item Id="{b3ec38a7-ed71-4ab9-9a43-1623fc3d4c30}">
<Expansion>//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="«?FileBase»"
// Project: StgSharp
// AuthorGroup: «?GetUserInfo(Company)»
// Copyright (c) «?GetUserInfo(Company)». All rights reserved.
//
//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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------</Expansion>
</Item>
</Items>
</Item>
<Item Id="{5d7e8673-3c4d-4f8c-98e5-96799d699c6b}" />
<Item Id="{871cff6e-18a7-44e2-9980-cf75ac927e7d}" />
<Item Id="{fc65f206-ebed-4567-b2ad-5da5fe2d5383}" />
<Item Id="{a4ca82b6-2912-47f4-bc3e-fa61fa152c64}" />
<Item Id="{55344716-9e28-4e48-b69f-4ee8c28127cb}" />
<Item Id="{cf975a83-9d8e-420e-a655-27d52cf13c09}" />
<Item Id="{83aac52b-9b01-47e7-932a-8b56e8b86b10}" />
<Item Id="{dc9f70e9-7053-45ed-90a6-e8257db37251}" />
<Item Id="{bfc86380-a990-47cc-9262-d6d644337e35}" />
<Item Id="{2830b620-b907-4390-8eda-c030402a9968}" />
<Item Id="{efc78e7d-fbe7-4a4f-a489-3ff6b881442b}" />
<Item Id="{336f73d0-1064-4ced-99a4-a36400438379}" />
<Item Id="{5babdc02-57e8-4854-82f7-9a9b97e48dad}" />
<Item Id="{8f6f1bb6-a38a-4c81-ae9b-f77a8709a73d}" />
<Item Id="{b0fb5608-05db-47c4-94b4-2bc7f72bc642}" />
<Item Id="{a43c547a-2d44-46eb-b39b-d39f7eb65a24}" />
<Item Id="{890dc8c3-dd34-489e-996a-4f3651f3e72a}" />
<Item Id="{d420e166-39c2-4ea1-a7b2-36416f33de4f}" />
<Item Id="{e4028d8a-d830-4882-b3ac-88e9913d996c}" />
<Item Id="{ed44f8f7-61d8-4932-ac14-6fb30ff55efb}" />
<Item Id="{d7b9b3af-94a6-4c1b-92c8-8058d1d71f0e}" />
<Item Id="{e91be916-8c04-4ebd-a9af-ddc38a046272}" />
<Item Id="{65b10956-7ae0-44ed-a376-6195f7e4d953}" />
</Templates>
</Item>
<Item Id="{8e4132de-c894-486f-8004-e2d08d0ba979}" />
<Item Id="{2433228d-3ae2-4022-aa52-7ab7a1a3116b}" />
<Item Id="{1379a9c1-e7a0-4872-87e5-24ef7c4a1315}" />
<Item Id="{80783537-1b32-4a51-b716-b32036574f4f}" />
<Item Id="{6b1c63bd-043f-4639-b757-a0c6132270fa}" />
<Item Id="{2ff95045-9084-4395-93a8-70d11cc8cdeb}" />
<Item Id="{633d268e-6d35-46c7-ac5d-d2885ca35a0d}" />
<Item Id="{34b6c10a-ee60-4c30-9f5c-e0c033ce7867}" />
<Item Id="{e63d4dbd-49ce-4fd6-ab9d-99e0bfc60b35}" />
<Item Id="{f4703284-a160-4272-be22-3366ade5e43f}" />
<Item Id="{51f7a921-200c-43e3-944c-54521ed0c201}" />
<Item Id="{08077498-5ebe-4300-ad58-bcbf5a3ebac3}" />
<Item Id="{90ee1142-998a-40f3-8aa1-fe850f766551}" />
<Item Id="{662a95d0-4d80-484b-b649-044f1464ff4a}" />
<Item Id="{9c9bbf9d-c82c-450d-bafa-2d8b1f6a4a63}" />
<Item Id="{78fc30d9-aa85-4d78-a3f0-8eeb5a2e1496}" />
<Item Id="{cb08e1a8-1829-4409-87e1-d9a9d3eea8a3}" />
<Item Id="{96418b0c-83d3-4656-90c9-59d4139e045c}" />
<Item Id="{447c50a1-8ec8-44ee-9915-7f0548350093}" />
<Item Id="{a641e756-107f-4e82-9952-010819e35abe}" />
<Item Id="{4cada8e9-90e0-4fe3-8171-91a3735d1447}" />
<Item Id="{3888be66-16ab-4a3e-b416-144246345fa0}" />
<Item Id="{0158dfdd-6429-462c-931f-1a37959d3b42}" />
<Item Id="{bf0539d1-191e-4764-b583-c95277cfa4bf}" />
</Categories>
</RootCategory>
</Options>
</Root>
+31 -31
View File
@@ -1,32 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.Formatting.UI.Options.SpacesOptionsContainer">
<Options Language="C#">
<Options>
<Item Id="{a4f7d021-5762-49a9-956e-040794d03b98}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.BinaryExpressionSpacesOption" />
<Item Id="{a957508f-bb2d-484c-b792-7eba9a44f0d4}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.RelationalExpressionSpacesOption" />
<Item Id="{12b5408f-751d-4709-908f-d269e40b26ab}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.BitwiseExpressionSpacesOption" />
<Item Id="{1be4039d-43ba-4867-b1c6-1453ef76638b}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.LogicalExpressionSpacesOption" />
<Item Id="{8e1642cf-2fd0-4bd8-b0f9-a289bf7f889a}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.UnaryExpressionSpacesOption" />
<Item Id="{43888577-23ae-4e8c-9103-b0e8b006dffd}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.TernaryExpressionSpacesOption" />
<Item Id="{6bd37f4f-1c00-4c06-a83a-2746936e6c8b}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.AssignmentExpressionSpacesOption" />
<Item Id="{07d202aa-0580-489b-9883-c3a9d2f750f6}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.ExpressionSpacesOption" />
<Item Id="{b6cc94db-d940-47bc-9e4c-46e7d397e829}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.StatementSpacesOption" />
<Item Id="{20314921-1cb7-4b58-a259-e7200cfd1014}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.ArgumentListSpacesOption" />
<Item Id="{b031d570-6511-4aa1-8dbb-075d09d9ed72}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.ParameterListSpacesOption" />
<Item Id="{ca5e97ed-0399-47cd-8bc2-8c2677f273ed}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.InitializerSpacesOption" />
<Item Id="{bd007738-8ed3-480b-8618-0c8841c68fb1}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.LambdaExpressionSpacesOption" />
<Item Id="{e677064b-b741-49b6-b6ad-b2634258bbed}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.ArrowExpressionSpacesOption" />
<Item Id="{1ee97312-d3a8-49b0-b557-e396e73a7898}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.TypeParameterListSpacesOption" />
<Item Id="{3d7f395a-bb87-43c2-b483-45b0e5662607}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.TypeArgumentListSpacesOption" />
<Item Id="{a8d62687-d898-460d-9322-1088c09f5d9b}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.AttributeListSpacesOption" />
<Item Id="{35f5e5b0-a043-4989-8680-da5aa592ece0}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CoalesceExpressionSpacesOption" />
<Item Id="{c63dcf47-261a-4a10-9a4d-5ca4957f5061}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.BaseListSpacesOption" />
<Item Id="{6c1b5b15-b7c8-4b91-ad28-a52e7389dc9b}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CtorInitSpacesOption" />
<Item Id="{10781ebe-9235-4229-a95b-a184827b9815}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CommentSpacesOption" />
<Item Id="{30a037ea-69da-4198-98ad-bc7342242e53}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.SwitchExpressionSpacesOption" />
<Item Id="{7a5f4d31-4fc4-4c34-ba85-2baad09873b4}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CollectionExpressionSpacesOption">
<WithinBrackets>False</WithinBrackets>
</Item>
</Options>
</Options>
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.Formatting.UI.Options.SpacesOptionsContainer">
<Options Language="C#">
<Options>
<Item Id="{a4f7d021-5762-49a9-956e-040794d03b98}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.BinaryExpressionSpacesOption" />
<Item Id="{a957508f-bb2d-484c-b792-7eba9a44f0d4}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.RelationalExpressionSpacesOption" />
<Item Id="{12b5408f-751d-4709-908f-d269e40b26ab}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.BitwiseExpressionSpacesOption" />
<Item Id="{1be4039d-43ba-4867-b1c6-1453ef76638b}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.LogicalExpressionSpacesOption" />
<Item Id="{8e1642cf-2fd0-4bd8-b0f9-a289bf7f889a}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.UnaryExpressionSpacesOption" />
<Item Id="{43888577-23ae-4e8c-9103-b0e8b006dffd}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.TernaryExpressionSpacesOption" />
<Item Id="{6bd37f4f-1c00-4c06-a83a-2746936e6c8b}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.AssignmentExpressionSpacesOption" />
<Item Id="{07d202aa-0580-489b-9883-c3a9d2f750f6}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.ExpressionSpacesOption" />
<Item Id="{b6cc94db-d940-47bc-9e4c-46e7d397e829}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.StatementSpacesOption" />
<Item Id="{20314921-1cb7-4b58-a259-e7200cfd1014}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.ArgumentListSpacesOption" />
<Item Id="{b031d570-6511-4aa1-8dbb-075d09d9ed72}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.ParameterListSpacesOption" />
<Item Id="{ca5e97ed-0399-47cd-8bc2-8c2677f273ed}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.InitializerSpacesOption" />
<Item Id="{bd007738-8ed3-480b-8618-0c8841c68fb1}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.LambdaExpressionSpacesOption" />
<Item Id="{e677064b-b741-49b6-b6ad-b2634258bbed}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.ArrowExpressionSpacesOption" />
<Item Id="{1ee97312-d3a8-49b0-b557-e396e73a7898}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.TypeParameterListSpacesOption" />
<Item Id="{3d7f395a-bb87-43c2-b483-45b0e5662607}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.TypeArgumentListSpacesOption" />
<Item Id="{a8d62687-d898-460d-9322-1088c09f5d9b}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.AttributeListSpacesOption" />
<Item Id="{35f5e5b0-a043-4989-8680-da5aa592ece0}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CoalesceExpressionSpacesOption" />
<Item Id="{c63dcf47-261a-4a10-9a4d-5ca4957f5061}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.BaseListSpacesOption" />
<Item Id="{6c1b5b15-b7c8-4b91-ad28-a52e7389dc9b}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CtorInitSpacesOption" />
<Item Id="{10781ebe-9235-4229-a95b-a184827b9815}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CommentSpacesOption" />
<Item Id="{30a037ea-69da-4198-98ad-bc7342242e53}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.SwitchExpressionSpacesOption" />
<Item Id="{7a5f4d31-4fc4-4c34-ba85-2baad09873b4}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CollectionExpressionSpacesOption">
<WithinBrackets>False</WithinBrackets>
</Item>
</Options>
</Options>
</Root>
+37 -37
View File
@@ -1,38 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.Formatting.UI.Options.WrappingOptionsContainer">
<Options Language="C#">
<Options>
<Item Id="{48f3f6c0-b3c2-4567-b071-33af7480c9a3}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.QueryExpressionFormattingOption" />
<Item Id="{a2c75ad7-ba6a-49ca-8070-6d9e3c52e3c6}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.BinaryExpressionFormattingOption" />
<Item Id="{0e2d0391-24ba-4ed8-aeb4-1b3e9f26be55}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.RelationalExpressionFormattingOption" />
<Item Id="{fea50ebc-99c9-4599-9b55-bf2961b9cc95}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.BitwiseExpressionFormattingOption" />
<Item Id="{4246b900-cbb3-44f4-b59e-b3055fb4f7bc}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.LogicalExpressionFormattingOption" />
<Item Id="{cd721fc1-23fb-4d88-a9f0-42d6b318a32b}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.TernaryExpressionFormattingOption" />
<Item Id="{10e904ea-f51d-4c32-9320-1b93fa557564}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.AssignmentExpressionFormattingOption" />
<Item Id="{25746bdc-0a43-4741-a740-20784691314b}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.InvocationArgumentListFormattingOption">
<WrapOpenParen>
<Option>NoWrap</Option>
</WrapOpenParen>
<WrapArgSeparator>
<Option>SimpleWrap</Option>
<Kind>WrapAfter</Kind>
</WrapArgSeparator>
<ArgCountTolerance>4</ArgCountTolerance>
</Item>
<Item Id="{D5EE2ECB-9FA6-47F7-A11F-007FC45E872D}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.AttributeArgumentsFormattingOption" />
<Item Id="{5A186AB9-03A5-4BFE-83B1-B99B3B8408BC}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.DeclarationParameterListFormattingOption" />
<Item Id="{8739877d-3674-40ae-90c4-38b2b160e92b}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.ArrayInitializerFormattingOption" />
<Item Id="{a940ae15-f98a-4b7d-8357-2214eb421250}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CollectionInitializerFormattingOption" />
<Item Id="{0414181a-3915-4d0c-81d3-3bc7718da90f}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.ObjectInitializerFormattingOption" />
<Item Id="{ea60e2ab-d67a-45e8-ab42-36d747c494c}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.ChainedMethodCallsFormattingOption" />
<Item Id="{5f4ed422-88b9-4e95-8401-cef404d645d6}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.LambdaExpressionFormattingOption" />
<Item Id="{0531de5e-ff20-4043-a67c-eeef76a8f3c3}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.ArrowExpressionFormattingOption" />
<Item Id="{08fa5953-cc71-4ded-bd41-ef5d5b690585}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.TypeParameterListFormattingOption" />
<Item Id="{dc060d33-cfc0-411e-9508-8d27b84934e8}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CoalesceExpressionFormattingOption" />
<Item Id="{b25d25a0-dd92-45ed-88ef-51953e28222c}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.BaseListWrappingFormattingOption" />
<Item Id="{4db5e14f-8f4c-416f-9b59-7e9239f2150e}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CtorInitWrappingFormattingOption" />
<Item Id="{be0c2117-5732-40fd-9b4a-0fe749356e87}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.SwitchExpressionWrappingFormattingOption" />
<Item Id="{099d9359-1af3-40a2-8841-50f6cc9527e1}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CollectionExpressionWrappingFormattingOption" />
</Options>
</Options>
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.Formatting.UI.Options.WrappingOptionsContainer">
<Options Language="C#">
<Options>
<Item Id="{48f3f6c0-b3c2-4567-b071-33af7480c9a3}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.QueryExpressionFormattingOption" />
<Item Id="{a2c75ad7-ba6a-49ca-8070-6d9e3c52e3c6}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.BinaryExpressionFormattingOption" />
<Item Id="{0e2d0391-24ba-4ed8-aeb4-1b3e9f26be55}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.RelationalExpressionFormattingOption" />
<Item Id="{fea50ebc-99c9-4599-9b55-bf2961b9cc95}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.BitwiseExpressionFormattingOption" />
<Item Id="{4246b900-cbb3-44f4-b59e-b3055fb4f7bc}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.LogicalExpressionFormattingOption" />
<Item Id="{cd721fc1-23fb-4d88-a9f0-42d6b318a32b}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.TernaryExpressionFormattingOption" />
<Item Id="{10e904ea-f51d-4c32-9320-1b93fa557564}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.AssignmentExpressionFormattingOption" />
<Item Id="{25746bdc-0a43-4741-a740-20784691314b}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.InvocationArgumentListFormattingOption">
<WrapOpenParen>
<Option>NoWrap</Option>
</WrapOpenParen>
<WrapArgSeparator>
<Option>SimpleWrap</Option>
<Kind>WrapAfter</Kind>
</WrapArgSeparator>
<ArgCountTolerance>4</ArgCountTolerance>
</Item>
<Item Id="{D5EE2ECB-9FA6-47F7-A11F-007FC45E872D}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.AttributeArgumentsFormattingOption" />
<Item Id="{5A186AB9-03A5-4BFE-83B1-B99B3B8408BC}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.DeclarationParameterListFormattingOption" />
<Item Id="{8739877d-3674-40ae-90c4-38b2b160e92b}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.ArrayInitializerFormattingOption" />
<Item Id="{a940ae15-f98a-4b7d-8357-2214eb421250}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CollectionInitializerFormattingOption" />
<Item Id="{0414181a-3915-4d0c-81d3-3bc7718da90f}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.ObjectInitializerFormattingOption" />
<Item Id="{ea60e2ab-d67a-45e8-ab42-36d747c494c}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.ChainedMethodCallsFormattingOption" />
<Item Id="{5f4ed422-88b9-4e95-8401-cef404d645d6}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.LambdaExpressionFormattingOption" />
<Item Id="{0531de5e-ff20-4043-a67c-eeef76a8f3c3}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.ArrowExpressionFormattingOption" />
<Item Id="{08fa5953-cc71-4ded-bd41-ef5d5b690585}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.TypeParameterListFormattingOption" />
<Item Id="{dc060d33-cfc0-411e-9508-8d27b84934e8}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CoalesceExpressionFormattingOption" />
<Item Id="{b25d25a0-dd92-45ed-88ef-51953e28222c}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.BaseListWrappingFormattingOption" />
<Item Id="{4db5e14f-8f4c-416f-9b59-7e9239f2150e}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CtorInitWrappingFormattingOption" />
<Item Id="{be0c2117-5732-40fd-9b4a-0fe749356e87}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.SwitchExpressionWrappingFormattingOption" />
<Item Id="{099d9359-1af3-40a2-8841-50f6cc9527e1}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CollectionExpressionWrappingFormattingOption" />
</Options>
</Options>
</Root>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.VisualizePlugins.RightMarginLine.Options.RightMarginLineOptions">
<Options Language="Neutral">
<Column>120</Column>
</Options>
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.VisualizePlugins.RightMarginLine.Options.RightMarginLineOptions">
<Options Language="Neutral">
<Column>120</Column>
</Options>
</Root>
+125 -125
View File
@@ -1,126 +1,126 @@
[*]
end_of_line = lf
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
[*.{bat,cmd,ps1}]
end_of_line = crlf
[*.cs]
# CS1591: 缺少对公共可见类型或成员的 XML 注释
dotnet_diagnostic.CS1591.severity = suggestion
[*.cs]
#### 命名样式 ####
# 命名规则
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# 符号规范
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# 命名样式
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
csharp_using_directive_placement = outside_namespace:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_conditional_delegate_call = true:suggestion
csharp_style_var_for_built_in_types = false:silent
csharp_style_var_when_type_is_apparent = false:silent
csharp_style_var_elsewhere = false:silent
csharp_prefer_simple_using_statement = false:suggestion
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = false:silent
csharp_style_prefer_primary_constructors = true:suggestion
csharp_prefer_static_local_function = true:suggestion
csharp_prefer_static_anonymous_function = true:suggestion
csharp_style_prefer_readonly_struct = true:suggestion
csharp_space_around_binary_operators = before_and_after
csharp_indent_labels = one_less_than_current
[*.vb]
#### 命名样式 ####
# 命名规则
dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion
dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface
dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
# 符号规范
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.类型.required_modifiers =
dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method
dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.非字段成员.required_modifiers =
# 命名样式
dotnet_naming_style.以_i_开始.required_prefix = I
dotnet_naming_style.以_i_开始.required_suffix =
dotnet_naming_style.以_i_开始.word_separator =
dotnet_naming_style.以_i_开始.capitalization = pascal_case
dotnet_naming_style.帕斯卡拼写法.required_prefix =
[*]
end_of_line = lf
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
[*.{bat,cmd,ps1}]
end_of_line = crlf
[*.cs]
# CS1591: 缺少对公共可见类型或成员的 XML 注释
dotnet_diagnostic.CS1591.severity = suggestion
[*.cs]
#### 命名样式 ####
# 命名规则
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# 符号规范
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# 命名样式
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
csharp_using_directive_placement = outside_namespace:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_conditional_delegate_call = true:suggestion
csharp_style_var_for_built_in_types = false:silent
csharp_style_var_when_type_is_apparent = false:silent
csharp_style_var_elsewhere = false:silent
csharp_prefer_simple_using_statement = false:suggestion
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = false:silent
csharp_style_prefer_primary_constructors = true:suggestion
csharp_prefer_static_local_function = true:suggestion
csharp_prefer_static_anonymous_function = true:suggestion
csharp_style_prefer_readonly_struct = true:suggestion
csharp_space_around_binary_operators = before_and_after
csharp_indent_labels = one_less_than_current
[*.vb]
#### 命名样式 ####
# 命名规则
dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion
dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface
dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
# 符号规范
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.类型.required_modifiers =
dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method
dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.非字段成员.required_modifiers =
# 命名样式
dotnet_naming_style.以_i_开始.required_prefix = I
dotnet_naming_style.以_i_开始.required_suffix =
dotnet_naming_style.以_i_开始.word_separator =
dotnet_naming_style.以_i_开始.capitalization = pascal_case
dotnet_naming_style.帕斯卡拼写法.required_prefix =
dotenv
+2 -2
View File
@@ -1,3 +1,3 @@
{
"help":"https://go.microsoft.com/fwlink/?linkid=866610"
{
"help":"https://go.microsoft.com/fwlink/?linkid=866610"
}
+14 -14
View File
@@ -1,14 +1,14 @@
/build
/.idea
/.vs
/.vscode
*/obj
*/bin
*/build
*/Debug
*/Release
/TestResults
*/x64
*/.vs
/StgSharp.Native/lib
obj
/build
/.idea
/.vs
/.vscode
*/obj
*/bin
*/build
*/Debug
*/Release
/TestResults
*/x64
*/.vs
/StgSharp.Native/lib
obj
+37 -37
View File
@@ -1,37 +1,37 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36705.20
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenerateGL", "StgSharp.GenerateGL\GenerateGL.csproj", "{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StgSharp.Common", "StgSharp.Common\StgSharp.Common.csproj", "{5D646C0E-CCF8-B5B4-7DE8-1C37542E7B4D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Debug|x64.ActiveCfg = Debug|x64
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Debug|x64.Build.0 = Debug|x64
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Release|Any CPU.Build.0 = Release|Any CPU
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Release|x64.ActiveCfg = Release|x64
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Release|x64.Build.0 = Release|x64
{5D646C0E-CCF8-B5B4-7DE8-1C37542E7B4D}.Debug|Any CPU.ActiveCfg = Debug|x64
{5D646C0E-CCF8-B5B4-7DE8-1C37542E7B4D}.Debug|x64.ActiveCfg = Debug|x64
{5D646C0E-CCF8-B5B4-7DE8-1C37542E7B4D}.Release|Any CPU.ActiveCfg = Release|x64
{5D646C0E-CCF8-B5B4-7DE8-1C37542E7B4D}.Release|x64.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AC71C01F-9867-4E43-85AB-4A8A7896B1BC}
EndGlobalSection
EndGlobal
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36705.20
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenerateGL", "StgSharp.GenerateGL\GenerateGL.csproj", "{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StgSharp.Common", "StgSharp.Common\StgSharp.Common.csproj", "{5D646C0E-CCF8-B5B4-7DE8-1C37542E7B4D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Debug|x64.ActiveCfg = Debug|x64
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Debug|x64.Build.0 = Debug|x64
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Release|Any CPU.Build.0 = Release|Any CPU
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Release|x64.ActiveCfg = Release|x64
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Release|x64.Build.0 = Release|x64
{5D646C0E-CCF8-B5B4-7DE8-1C37542E7B4D}.Debug|Any CPU.ActiveCfg = Debug|x64
{5D646C0E-CCF8-B5B4-7DE8-1C37542E7B4D}.Debug|x64.ActiveCfg = Debug|x64
{5D646C0E-CCF8-B5B4-7DE8-1C37542E7B4D}.Release|Any CPU.ActiveCfg = Release|x64
{5D646C0E-CCF8-B5B4-7DE8-1C37542E7B4D}.Release|x64.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AC71C01F-9867-4E43-85AB-4A8A7896B1BC}
EndGlobalSection
EndGlobal
+155 -155
View File
@@ -1,155 +1,155 @@
MIT License
Copyright (c) 2024 Nitload StgSharp Team
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.
StgSharp program uses the following open source projects,
I'd like to refer to them here and show my sincere appreciation,
while the names are sorted in no particular order
1. GLFW
Remote repository on: https://www.glfw.org/
License of GLFW:
Copyright (c) 2002-2006 Marcus Geelnard
Copyright (c) 2006-2019 Camilla Löwy
This software is provided 'as-is',
without any express or implied warranty.
In no event will the authors be held liable for
any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
The origin of this software must not be misrepresented;
you must not claim that you wrote the original software.
If you use this software in a product, an acknowledgment
in the product documentation would be appreciated but is not required.
Altered source versions must be plainly marked as such,
and must not be misrepresented as being the original software.
This notice may not be removed or altered from any source distribution.
2. GLAD, Khronos Specifications and other Specification headers
Web service available on https://gen.glad.sh/
The glad source code:
The MIT License (MIT)
Copyright (c) 2013-2022 David Herberth
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.
The Khronos Specifications:
Copyright (c) 2013-2020 The Khronos Group Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
3. docs.gl
4. Terminal.GUI
Remote repository on: https://github.com/gui-cs/Terminal.Gui
License of Terminal.GUI:
Copyright 2007-2011 Novell Inc
Copyright 2017 Microsoft Corp
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.
5. IxMilia.Step
License of IxMilia.Step:
The MIT License (MIT)
Copyright (c) IxMilia.
All rights reserved.
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.
MIT License
Copyright (c) 2024 Nitload StgSharp Team
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.
StgSharp program uses the following open source projects,
I'd like to refer to them here and show my sincere appreciation,
while the names are sorted in no particular order
1. GLFW
Remote repository on: https://www.glfw.org/
License of GLFW:
Copyright (c) 2002-2006 Marcus Geelnard
Copyright (c) 2006-2019 Camilla Löwy
This software is provided 'as-is',
without any express or implied warranty.
In no event will the authors be held liable for
any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
The origin of this software must not be misrepresented;
you must not claim that you wrote the original software.
If you use this software in a product, an acknowledgment
in the product documentation would be appreciated but is not required.
Altered source versions must be plainly marked as such,
and must not be misrepresented as being the original software.
This notice may not be removed or altered from any source distribution.
2. GLAD, Khronos Specifications and other Specification headers
Web service available on https://gen.glad.sh/
The glad source code:
The MIT License (MIT)
Copyright (c) 2013-2022 David Herberth
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.
The Khronos Specifications:
Copyright (c) 2013-2020 The Khronos Group Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
3. docs.gl
4. Terminal.GUI
Remote repository on: https://github.com/gui-cs/Terminal.Gui
License of Terminal.GUI:
Copyright 2007-2011 Novell Inc
Copyright 2017 Microsoft Corp
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.
5. IxMilia.Step
License of IxMilia.Step:
The MIT License (MIT)
Copyright (c) IxMilia.
All rights reserved.
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.
+239 -239
View File
@@ -1,240 +1,240 @@
# StgSharp
![StgSharp Logo](STG%23LOGO.png)
StgSharp is a modern C# framework that combines game engine capabilities with high-performance computing features. Originally designed as a next-generation STG (Shoot 'em Up) game engine, it has evolved to include advanced mathematical libraries, parallel computing capabilities, and comprehensive graphics APIs.
## Overview
StgSharp provides both low-level and high-level bindings to OpenGL, multimedia control, and device management, offering everything needed to create STG games and high-performance applications. The framework is built on .NET 8 and supports Windows and Linux platforms.
## Core Modules
### StgSharp.Common
The foundational library containing shared functionality:
- **Mathematics**: High-performance linear algebra, matrix operations, and mathematical utilities
- **Graphics**: OpenGL bindings, rendering pipeline, and graphics framework
- **High Performance**: SIMD operations, memory management, and parallel computing
- **Collections**: Specialized data structures and containers
- **MVVM**: Model-View-ViewModel framework for UI applications
- **Entities**: Game entity system and behavior management
- **Environment**: Platform abstraction and system integration
### StgSharp.Mathematics (Numeric)
Numerics and linear algebra built on top of the core library:
- **Matrix & Vector Types**: High-performance math primitives
- **Numeric Utilities**: Core numeric helpers and algorithms
- **SIMD-Friendly Kernels**: Data layouts optimized for hardware acceleration
### StgSharp.RegularAnalysis
Regular-language and parsing infrastructure (replaces the former Script module):
- **Regex/Automata Core**: Regular expression processing and automata utilities
- **Parsing Primitives**: Reusable tokenizer and grammar building blocks
- **Analysis Pipeline**: Structured processing for language-like inputs
### StgSharp.Native
Native library integration:
- **GLFW Integration**: Cross-platform window and input management
- **SIMD Operations**: Hardware-accelerated vector operations
- **Matrix Kernels**: Optimized matrix computation routines
- **Context Management**: Graphics context and resource management
### StgSharp.Device
Low-level device and port abstractions:
- **Command Interfaces**: Command shapes and serialization contracts
- **Port Control**: Stream-oriented device access
- **Decoder/Response Models**: Protocol-level data handling
### StgSharp.TerminalDialogue
Terminal-based user interface application and components:
- **Interactive Terminal**: Command-line UI components
- **Dialogue System**: User interaction and input handling
- **Terminal Graphics**: Text-based UI rendering
> Note: The legacy Script module has been superseded by RegularAnalysis. The Model module is under refactoring and is currently out of the core framework.
## Key Features
### High-Performance Computing
- **Parallel Matrix Operations**: Multi-threaded matrix computations with optimized scheduling
- **SIMD Acceleration**: Hardware-optimized vector operations
- **Memory Management**: Custom allocators including HLSF (Hybrid Layer Segregated Fit)
- **Thread Pool Management**: Efficient task scheduling and execution
#### HLSF Benchmark Snapshot (BenchmarkDotNet)
```
BenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.7623)
AMD Ryzen 7 6800H with Radeon Graphics, 1 CPU, 16 logical and 8 physical cores
.NET SDK 10.0.102
[Host] : .NET 8.0.23 (8.0.2325.60607), X64 RyuJIT AVX2
Toolchain=InProcessEmitToolchain InvocationCount=2048 UnrollFactor=1
```
| Method | Mean | Error | StdDev | Ratio | RatioSD |
|------- |-----:|-----:|------:|------:|-------:|
| Hlsf | 76.23 µs | 0.355 µs | 0.315 µs | 0.44 | 0.01 |
| Libc | 172.98 µs | 3.042 µs | 2.988 µs | 1.00 | 0.02 |
This benchmark measures steady-state small-block allocation throughput (64B–4KB),
which represents the dominant allocation pattern in StgSharp's matrix computation pipeline.
HLSF uses a pre-allocated arena with size-class free lists, avoiding kernel-mode allocation calls.
Under this workload, HLSF achieves ~2.3× throughput over system malloc. This is not a general-purpose allocator comparison.
### Graphics and Rendering
- **OpenGL Integration**: Complete OpenGL API coverage
- **Cross-Platform Support**: Windows and Linux compatibility
- **Rendering Pipeline**: Flexible and extensible graphics framework
- **Shader Management**: Dynamic shader compilation and management
### Game Engine Capabilities
- **Entity System**: Flexible game object management
- **State Machine**: Game state and behavior management
- **Input Handling**: Comprehensive input and control systems
- **Resource Management**: Efficient asset loading and management
### Data Processing
- **STEP File Support**: Industrial data format processing
- **EXPRESS Language**: Domain-specific language compilation
- **Pipeline Processing**: Efficient data transformation workflows
## Requirements
- .NET 8.0 or later
- Windows 10/11 or Linux
- x64 or ARM64 architecture
- OpenGL 3.3+ support
## Installation
StgSharp is currently in development and requires compilation from source. **NuGet packages are not yet available** - you must build the project from source code.
### Prerequisites
- **Windows**: Visual Studio 2022 or later with C++ development tools
- **Linux**: CMake and compatible C++ compiler (planned support)
- .NET 8.0 SDK or later
- Git for source code retrieval
### Building from Source
1. **Clone the repository**:
```bash
git clone https://github.com/Nitload-NSI/StgSharp.git
cd StgSharp
```
2. **Windows (Recommended)**:
- Open `StgSharp.sln` in Visual Studio
- Build the solution (Debug or Release configuration)
- The native components will be automatically compiled
3. **Linux (Experimental)**:
```bash
# Build .NET components only
# Note: Native components are not yet supported on Linux
dotnet build
```
### Build Configuration
- **Debug**: Full debugging symbols, unoptimized
- **Release**: Optimized for performance
### Notes
- Windows compilation with Visual Studio is fully supported and tested
- Linux support is experimental and limited to .NET components only
- Native components (StgSharp.Native) are currently Windows-only and require MSVC
- CMake support for native components is not yet implemented
- Some features may not be available on all platforms
## Quick Start
### Basic Graphics Application
```csharp
/*To be implemented*/
```
### Matrix Operations
```csharp
using StgSharp.Mathematics;
// Create matrices
var matrixA = Matrix<float>.Create(4, 4);
var matrixB = Matrix<float>.Create(4, 4);
// Perform parallel matrix multiplication
var result = matrixA * matrixB;
```
### Memory Management
```csharp
using StgSharp.HighPerformance.Memory;
// Create high-performance allocator
using var allocator = new HybridLayerSegregatedFitAllocator(64 * 1024 * 1024);
// Allocate memory
var handle = allocator.Alloc(1024);
// Use allocated memory
allocator.Free(handle);
```
## Documentation
### Technical Documentation
- [HLSF Allocator](StgSharp.Common/HighPerformance/Memory/InroductionToHLSF.md) - High-performance memory allocator documentation
- [Native Library Naming](StgSharp.Native/naming.md) - Native library file naming conventions
## Contributing
StgSharp is developed by Nitload Space. Contributions are welcome, particularly in:
- Performance optimizations
- Cross-platform compatibility
- Documentation improvements
- New feature implementations
## License
StgSharp is licensed under the MIT License. See [LICENSE](LICENSE) for details.
## Future Plans
- **Vulkan Support**: Planned for version 2.0
- **Enhanced Cross-Platform**: Improved Linux and mobile support
- **Performance Optimizations**: Further SIMD and parallel processing improvements
- **Extended Language Support**: Additional domain-specific languages
## Support
# StgSharp
![StgSharp Logo](STG%23LOGO.png)
StgSharp is a modern C# framework that combines game engine capabilities with high-performance computing features. Originally designed as a next-generation STG (Shoot 'em Up) game engine, it has evolved to include advanced mathematical libraries, parallel computing capabilities, and comprehensive graphics APIs.
## Overview
StgSharp provides both low-level and high-level bindings to OpenGL, multimedia control, and device management, offering everything needed to create STG games and high-performance applications. The framework is built on .NET 8 and supports Windows and Linux platforms.
## Core Modules
### StgSharp.Common
The foundational library containing shared functionality:
- **Mathematics**: High-performance linear algebra, matrix operations, and mathematical utilities
- **Graphics**: OpenGL bindings, rendering pipeline, and graphics framework
- **High Performance**: SIMD operations, memory management, and parallel computing
- **Collections**: Specialized data structures and containers
- **MVVM**: Model-View-ViewModel framework for UI applications
- **Entities**: Game entity system and behavior management
- **Environment**: Platform abstraction and system integration
### StgSharp.Mathematics (Numeric)
Numerics and linear algebra built on top of the core library:
- **Matrix & Vector Types**: High-performance math primitives
- **Numeric Utilities**: Core numeric helpers and algorithms
- **SIMD-Friendly Kernels**: Data layouts optimized for hardware acceleration
### StgSharp.RegularAnalysis
Regular-language and parsing infrastructure (replaces the former Script module):
- **Regex/Automata Core**: Regular expression processing and automata utilities
- **Parsing Primitives**: Reusable tokenizer and grammar building blocks
- **Analysis Pipeline**: Structured processing for language-like inputs
### StgSharp.Native
Native library integration:
- **GLFW Integration**: Cross-platform window and input management
- **SIMD Operations**: Hardware-accelerated vector operations
- **Matrix Kernels**: Optimized matrix computation routines
- **Context Management**: Graphics context and resource management
### StgSharp.Device
Low-level device and port abstractions:
- **Command Interfaces**: Command shapes and serialization contracts
- **Port Control**: Stream-oriented device access
- **Decoder/Response Models**: Protocol-level data handling
### StgSharp.TerminalDialogue
Terminal-based user interface application and components:
- **Interactive Terminal**: Command-line UI components
- **Dialogue System**: User interaction and input handling
- **Terminal Graphics**: Text-based UI rendering
> Note: The legacy Script module has been superseded by RegularAnalysis. The Model module is under refactoring and is currently out of the core framework.
## Key Features
### High-Performance Computing
- **Parallel Matrix Operations**: Multi-threaded matrix computations with optimized scheduling
- **SIMD Acceleration**: Hardware-optimized vector operations
- **Memory Management**: Custom allocators including HLSF (Hybrid Layer Segregated Fit)
- **Thread Pool Management**: Efficient task scheduling and execution
#### HLSF Benchmark Snapshot (BenchmarkDotNet)
```
BenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.7623)
AMD Ryzen 7 6800H with Radeon Graphics, 1 CPU, 16 logical and 8 physical cores
.NET SDK 10.0.102
[Host] : .NET 8.0.23 (8.0.2325.60607), X64 RyuJIT AVX2
Toolchain=InProcessEmitToolchain InvocationCount=2048 UnrollFactor=1
```
| Method | Mean | Error | StdDev | Ratio | RatioSD |
|------- |-----:|-----:|------:|------:|-------:|
| Hlsf | 76.23 µs | 0.355 µs | 0.315 µs | 0.44 | 0.01 |
| Libc | 172.98 µs | 3.042 µs | 2.988 µs | 1.00 | 0.02 |
This benchmark measures steady-state small-block allocation throughput (64B–4KB),
which represents the dominant allocation pattern in StgSharp's matrix computation pipeline.
HLSF uses a pre-allocated arena with size-class free lists, avoiding kernel-mode allocation calls.
Under this workload, HLSF achieves ~2.3× throughput over system malloc. This is not a general-purpose allocator comparison.
### Graphics and Rendering
- **OpenGL Integration**: Complete OpenGL API coverage
- **Cross-Platform Support**: Windows and Linux compatibility
- **Rendering Pipeline**: Flexible and extensible graphics framework
- **Shader Management**: Dynamic shader compilation and management
### Game Engine Capabilities
- **Entity System**: Flexible game object management
- **State Machine**: Game state and behavior management
- **Input Handling**: Comprehensive input and control systems
- **Resource Management**: Efficient asset loading and management
### Data Processing
- **STEP File Support**: Industrial data format processing
- **EXPRESS Language**: Domain-specific language compilation
- **Pipeline Processing**: Efficient data transformation workflows
## Requirements
- .NET 8.0 or later
- Windows 10/11 or Linux
- x64 or ARM64 architecture
- OpenGL 3.3+ support
## Installation
StgSharp is currently in development and requires compilation from source. **NuGet packages are not yet available** - you must build the project from source code.
### Prerequisites
- **Windows**: Visual Studio 2022 or later with C++ development tools
- **Linux**: CMake and compatible C++ compiler (planned support)
- .NET 8.0 SDK or later
- Git for source code retrieval
### Building from Source
1. **Clone the repository**:
```bash
git clone https://github.com/Nitload-NSI/StgSharp.git
cd StgSharp
```
2. **Windows (Recommended)**:
- Open `StgSharp.sln` in Visual Studio
- Build the solution (Debug or Release configuration)
- The native components will be automatically compiled
3. **Linux (Experimental)**:
```bash
# Build .NET components only
# Note: Native components are not yet supported on Linux
dotnet build
```
### Build Configuration
- **Debug**: Full debugging symbols, unoptimized
- **Release**: Optimized for performance
### Notes
- Windows compilation with Visual Studio is fully supported and tested
- Linux support is experimental and limited to .NET components only
- Native components (StgSharp.Native) are currently Windows-only and require MSVC
- CMake support for native components is not yet implemented
- Some features may not be available on all platforms
## Quick Start
### Basic Graphics Application
```csharp
/*To be implemented*/
```
### Matrix Operations
```csharp
using StgSharp.Mathematics;
// Create matrices
var matrixA = Matrix<float>.Create(4, 4);
var matrixB = Matrix<float>.Create(4, 4);
// Perform parallel matrix multiplication
var result = matrixA * matrixB;
```
### Memory Management
```csharp
using StgSharp.HighPerformance.Memory;
// Create high-performance allocator
using var allocator = new HybridLayerSegregatedFitAllocator(64 * 1024 * 1024);
// Allocate memory
var handle = allocator.Alloc(1024);
// Use allocated memory
allocator.Free(handle);
```
## Documentation
### Technical Documentation
- [HLSF Allocator](StgSharp.Common/HighPerformance/Memory/InroductionToHLSF.md) - High-performance memory allocator documentation
- [Native Library Naming](StgSharp.Native/naming.md) - Native library file naming conventions
## Contributing
StgSharp is developed by Nitload Space. Contributions are welcome, particularly in:
- Performance optimizations
- Cross-platform compatibility
- Documentation improvements
- New feature implementations
## License
StgSharp is licensed under the MIT License. See [LICENSE](LICENSE) for details.
## Future Plans
- **Vulkan Support**: Planned for version 2.0
- **Enhanced Cross-Platform**: Improved Linux and mobile support
- **Performance Optimizations**: Further SIMD and parallel processing improvements
- **Extended Language Support**: Additional domain-specific languages
## Support
For questions, issues, or contributions, please refer to the project repository or contact the development team.
+36 -36
View File
@@ -1,36 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.MSTest" Version="1.1.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\StgSharp.Common\StgSharp.Common.csproj" />
<ProjectReference Include="..\StgSharp.Model\StgSharp.Model.csproj" />
<ProjectReference Include="..\StgSharp.Script\StgSharp.Script.csproj" />
<ProjectReference Include="..\StgSharp.TerminalDialogue\StgSharp.TerminalDialogue.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="xcopy /E /I /Y $(SolutionDir)build $(OutDir)" />
</Target>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.MSTest" Version="1.1.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\StgSharp.Common\StgSharp.Common.csproj" />
<ProjectReference Include="..\StgSharp.Model\StgSharp.Model.csproj" />
<ProjectReference Include="..\StgSharp.Script\StgSharp.Script.csproj" />
<ProjectReference Include="..\StgSharp.TerminalDialogue\StgSharp.TerminalDialogue.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="xcopy /E /I /Y $(SolutionDir)build $(OutDir)" />
</Target>
</Project>
+117 -117
View File
@@ -1,118 +1,118 @@
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
// file="UnitTest1.cs"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
using StgSharp;
using StgSharp.Collections;
using StgSharp.Script;
using StgSharp.Script.Express;
//using StgSharp.Model;
using System.Collections.Concurrent;
using System.Text.RegularExpressions;
namespace STgSharpTest
{
[TestClass]
public class UnitTest1
{
string testExpPath = @"D:\Programmer\stepcode\data\ap203\ap203.exp";
[TestMethod]
public void RegexTest()
{
string sample = "FILE_DESCRIPTION(('CATIA V5 STEP Exchange'),'2;1');";
foreach( var match in Regex.Match(
sample, @"^(?<CALLER>[A-Z_]+)\s?\((?<PARAM>.+)\)\;" )
.Groups ) {
Console.WriteLine( $"{match.ToString()}" );
Console.WriteLine( "\t\t\t" );
}
}
[TestMethod]
public void TestSource()
{
ScriptSourceTransmitter st = new ScriptSourceTransmitter("end", 0);
for (int i = 0; i < 10; i++)
{
st.WriteLine($"Code{i} {new string(' ', i + 1)}-- Comment{i}");
}
st.WriteLine("Code (* comment");
for (int i = 0; i < 10; i++)
{
st.WriteLine($"comment-code");
}
st.WriteLine($"comment *) code");
for (int i = 0; i < 10; i++)
{
st.WriteLine($" Code{i} (* (* Comment *) *) Code{i}");
}
st.WriteLine("end");
ExpSourceReader reader = new ExpSourceReader(st);
while (!st.IsEmpty)
{
Console.WriteLine(new string(reader.ReadLine()));
}
}
[TestMethod]
public void TestMethod()
{
ScriptSourceTransmitter st = new ScriptSourceTransmitter("end", 0);
for (int i = 0; i < 10; i++)
{
st.WriteLine($"Code{i} {new string(' ',i+1)}-- Comment{i}");
}
st.WriteLine("Code (* comment");
for (int i = 0; i < 10; i++)
{
st.WriteLine($"comment-code");
}
st.WriteLine($"comment *) code");
for (int i = 0; i < 10; i++)
{
st.WriteLine($" Code{i} (* (* Comment *) *) Code{i}");
}
st.WriteLine("end");
ExpSourceReader reader = new ExpSourceReader(st);
while (!st.IsEmpty)
{
Console.WriteLine(reader.ReadToken().Value.ToString());
}
}
}
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
// file="UnitTest1.cs"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
using StgSharp;
using StgSharp.Collections;
using StgSharp.Script;
using StgSharp.Script.Express;
//using StgSharp.Model;
using System.Collections.Concurrent;
using System.Text.RegularExpressions;
namespace STgSharpTest
{
[TestClass]
public class UnitTest1
{
string testExpPath = @"D:\Programmer\stepcode\data\ap203\ap203.exp";
[TestMethod]
public void RegexTest()
{
string sample = "FILE_DESCRIPTION(('CATIA V5 STEP Exchange'),'2;1');";
foreach( var match in Regex.Match(
sample, @"^(?<CALLER>[A-Z_]+)\s?\((?<PARAM>.+)\)\;" )
.Groups ) {
Console.WriteLine( $"{match.ToString()}" );
Console.WriteLine( "\t\t\t" );
}
}
[TestMethod]
public void TestSource()
{
ScriptSourceTransmitter st = new ScriptSourceTransmitter("end", 0);
for (int i = 0; i < 10; i++)
{
st.WriteLine($"Code{i} {new string(' ', i + 1)}-- Comment{i}");
}
st.WriteLine("Code (* comment");
for (int i = 0; i < 10; i++)
{
st.WriteLine($"comment-code");
}
st.WriteLine($"comment *) code");
for (int i = 0; i < 10; i++)
{
st.WriteLine($" Code{i} (* (* Comment *) *) Code{i}");
}
st.WriteLine("end");
ExpSourceReader reader = new ExpSourceReader(st);
while (!st.IsEmpty)
{
Console.WriteLine(new string(reader.ReadLine()));
}
}
[TestMethod]
public void TestMethod()
{
ScriptSourceTransmitter st = new ScriptSourceTransmitter("end", 0);
for (int i = 0; i < 10; i++)
{
st.WriteLine($"Code{i} {new string(' ',i+1)}-- Comment{i}");
}
st.WriteLine("Code (* comment");
for (int i = 0; i < 10; i++)
{
st.WriteLine($"comment-code");
}
st.WriteLine($"comment *) code");
for (int i = 0; i < 10; i++)
{
st.WriteLine($" Code{i} (* (* Comment *) *) Code{i}");
}
st.WriteLine("end");
ExpSourceReader reader = new ExpSourceReader(st);
while (!st.IsEmpty)
{
Console.WriteLine(reader.ReadToken().Value.ToString());
}
}
}
}
+155 -155
View File
@@ -1,155 +1,155 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HlsfAllocatorBench"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using BenchmarkDotNet.Attributes;
using StgSharp.Mathematics.Memory;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace StgSharp.Benchmark
{
[MemoryDiagnoser]
[InvocationCount(2048)]
public class HlsfAllocatorBench
{
private const uint Alignment = 64;
private const int AllocationCount = 2048;
private static readonly int[] AllocSizes = [
64,
96,
150,
200,
400,
1024,
4096
];
private HybridLayerSegregatedFitAllocator _hlsfAllocator = null!;
private readonly List<HybridLayerSegregatedFitAllocationHandle> _hlsfLocalList = new();
private readonly List<nint> _libcLocalList = new();
private Random _random = new(42);
[GlobalCleanup]
public void Cleanup()
{
_hlsfAllocator?.Dispose();
_hlsfLocalList.Clear();
_hlsfLocalList.TrimExcess();
_libcLocalList.Clear();
_libcLocalList.TrimExcess();
}
[Benchmark]
public void Hlsf()
{
RunHlsf(_hlsfAllocator, FreePolicy.NoCollect);
}
[IterationSetup]
public void IterationSetup()
{
_hlsfLocalList.Clear();
_libcLocalList.Clear();
}
[Benchmark(Baseline = true)]
public unsafe void Libc()
{
_libcLocalList.Clear();
for (int i = 0; i < AllocationCount; i++)
{
uint size = AlignSize(GetNextRandomSize());
void* allocate = NativeMemory.AlignedAlloc(size, Alignment);
if (allocate == null) {
throw new OutOfMemoryException("NativeMemory.AlignedAlloc returned null.");
}
_libcLocalList.Add((nint)allocate);
}
for (int i = 0; i < _libcLocalList.Count; i++) {
NativeMemory.AlignedFree((void*)_libcLocalList[i]);
}
}
[GlobalSetup]
public void Setup()
{
_random = new Random(42);
_hlsfAllocator = new HybridLayerSegregatedFitAllocator(GetArenaBytes(), align:(int)Alignment);
}
private static uint AlignSize(
uint size
)
{
uint mask = Alignment - 1;
return (size + mask) & ~mask;
}
private static nuint GetArenaBytes()
{
const int defaultMb = 512;
const int minMb = 64;
int mb = defaultMb;
string? env = Environment.GetEnvironmentVariable("HLSF_BENCH_ARENA_MB");
if (!string.IsNullOrWhiteSpace(env) &&
int.TryParse(env, out int parsed) &&
parsed >= minMb) {
mb = parsed;
}
return (nuint)mb * 1024u * 1024u;
}
private uint GetNextRandomSize() => (uint)AllocSizes[_random.Next(AllocSizes.Length)];
private void RunHlsf(
HybridLayerSegregatedFitAllocator allocator,
FreePolicy policy
)
{
_hlsfLocalList.Clear();
for (int i = 0; i < AllocationCount; i++)
{
uint size = AlignSize(GetNextRandomSize());
HybridLayerSegregatedFitAllocationHandle allocate = allocator.Alloc(size);
_hlsfLocalList.Add(allocate);
}
for (int i = 0; i < _hlsfLocalList.Count; i++) {
allocator.Free(_hlsfLocalList[i], policy);
}
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HlsfAllocatorBench"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using BenchmarkDotNet.Attributes;
using StgSharp.Mathematics.Memory;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace StgSharp.Benchmark
{
[MemoryDiagnoser]
[InvocationCount(2048)]
public class HlsfAllocatorBench
{
private const uint Alignment = 64;
private const int AllocationCount = 2048;
private static readonly int[] AllocSizes = [
64,
96,
150,
200,
400,
1024,
4096
];
private HybridLayerSegregatedFitAllocator _hlsfAllocator = null!;
private readonly List<HybridLayerSegregatedFitAllocationHandle> _hlsfLocalList = new();
private readonly List<nint> _libcLocalList = new();
private Random _random = new(42);
[GlobalCleanup]
public void Cleanup()
{
_hlsfAllocator?.Dispose();
_hlsfLocalList.Clear();
_hlsfLocalList.TrimExcess();
_libcLocalList.Clear();
_libcLocalList.TrimExcess();
}
[Benchmark]
public void Hlsf()
{
RunHlsf(_hlsfAllocator, FreePolicy.NoCollect);
}
[IterationSetup]
public void IterationSetup()
{
_hlsfLocalList.Clear();
_libcLocalList.Clear();
}
[Benchmark(Baseline = true)]
public unsafe void Libc()
{
_libcLocalList.Clear();
for (int i = 0; i < AllocationCount; i++)
{
uint size = AlignSize(GetNextRandomSize());
void* allocate = NativeMemory.AlignedAlloc(size, Alignment);
if (allocate == null) {
throw new OutOfMemoryException("NativeMemory.AlignedAlloc returned null.");
}
_libcLocalList.Add((nint)allocate);
}
for (int i = 0; i < _libcLocalList.Count; i++) {
NativeMemory.AlignedFree((void*)_libcLocalList[i]);
}
}
[GlobalSetup]
public void Setup()
{
_random = new Random(42);
_hlsfAllocator = new HybridLayerSegregatedFitAllocator(GetArenaBytes(), align:(int)Alignment);
}
private static uint AlignSize(
uint size
)
{
uint mask = Alignment - 1;
return (size + mask) & ~mask;
}
private static nuint GetArenaBytes()
{
const int defaultMb = 512;
const int minMb = 64;
int mb = defaultMb;
string? env = Environment.GetEnvironmentVariable("HLSF_BENCH_ARENA_MB");
if (!string.IsNullOrWhiteSpace(env) &&
int.TryParse(env, out int parsed) &&
parsed >= minMb) {
mb = parsed;
}
return (nuint)mb * 1024u * 1024u;
}
private uint GetNextRandomSize() => (uint)AllocSizes[_random.Next(AllocSizes.Length)];
private void RunHlsf(
HybridLayerSegregatedFitAllocator allocator,
FreePolicy policy
)
{
_hlsfLocalList.Clear();
for (int i = 0; i < AllocationCount; i++)
{
uint size = AlignSize(GetNextRandomSize());
HybridLayerSegregatedFitAllocationHandle allocate = allocator.Alloc(size);
_hlsfLocalList.Add(allocate);
}
for (int i = 0; i < _hlsfLocalList.Count; i++) {
allocator.Free(_hlsfLocalList[i], policy);
}
}
}
}
+52 -52
View File
@@ -1,52 +1,52 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="Program"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Toolchains.InProcess.Emit;
using StgSharp.Benchmark;
namespace StgSharp.Benchmark
{
internal static class Program
{
private static void Main(
string[] args
)
{
IConfig config = ManualConfig.Create(DefaultConfig.Instance)
.AddLogger(ConsoleLogger.Default)
.AddJob(Job.Default
.WithToolchain(InProcessEmitToolchain.Instance));
_ = BenchmarkRunner.Run<SwissTableBench>(config);
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="Program"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Toolchains.InProcess.Emit;
using StgSharp.Benchmark;
namespace StgSharp.Benchmark
{
internal static class Program
{
private static void Main(
string[] args
)
{
IConfig config = ManualConfig.Create(DefaultConfig.Instance)
.AddLogger(ConsoleLogger.Default)
.AddJob(Job.Default
.WithToolchain(InProcessEmitToolchain.Instance));
_ = BenchmarkRunner.Run<SwissTableBench>(config);
}
}
}
+24 -24
View File
@@ -1,24 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<SolutionDir Condition="'$(SolutionDir)'=='' Or '$(SolutionDir)'=='*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\StgSharp.Common\StgSharp.Common.csproj" Properties="DisablePostBuildCopy=true" />
</ItemGroup>
<ItemGroup>
<NativeArtifacts Include="$(SolutionDir)build\**\StgSharp.Native*.dll" />
<NativeArtifacts Include="$(SolutionDir)build\**\StgSharp.Native*.pdb" />
</ItemGroup>
<Target Name="CopyNativeArtifacts" AfterTargets="Build" Condition="'$(DisableNativeCopy)'!='true'">
<Copy SourceFiles="@(NativeArtifacts)" DestinationFolder="$(TargetDir)" SkipUnchangedFiles="true" Condition="@(NativeArtifacts) != ''" />
</Target>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<SolutionDir Condition="'$(SolutionDir)'=='' Or '$(SolutionDir)'=='*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\StgSharp.Common\StgSharp.Common.csproj" Properties="DisablePostBuildCopy=true" />
</ItemGroup>
<ItemGroup>
<NativeArtifacts Include="$(SolutionDir)build\**\StgSharp.Native*.dll" />
<NativeArtifacts Include="$(SolutionDir)build\**\StgSharp.Native*.pdb" />
</ItemGroup>
<Target Name="CopyNativeArtifacts" AfterTargets="Build" Condition="'$(DisableNativeCopy)'!='true'">
<Copy SourceFiles="@(NativeArtifacts)" DestinationFolder="$(TargetDir)" SkipUnchangedFiles="true" Condition="@(NativeArtifacts) != ''" />
</Target>
</Project>
@@ -1,194 +1,194 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="BidirectionalDictionary"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace StgSharp.Collections
{
public sealed class BidirectionalDictionary<TFirst, TSecond> : IBidirectionalDictionary<TFirst, TSecond>
where TFirst: notnull
where TSecond: notnull
{
private readonly Dictionary<TFirst, TSecond> _forward;
private readonly Dictionary<TSecond, TFirst> _reverse;
public BidirectionalDictionary()
{
_forward = [];
_reverse = [];
}
public TFirst this[TSecond key]
{
get => _reverse.TryGetValue(key, out TFirst value) ?
value :
throw new ArgumentException($"Cannot find key {key} in dictionary.");
set
{
if (_reverse.TryGetValue(key, out TFirst origin))
{
_reverse[key] = value;
_ = _forward.Remove(origin);
} else
{
_reverse.Add(key, value);
}
_forward.Add(value, key);
}
}
public TSecond this[TFirst key]
{
get => _forward.TryGetValue(key, out TSecond value) ?
value :
throw new ArgumentException($"Cannot find key {key} in dictionary.");
set
{
if (_forward.TryGetValue(key, out TSecond origin))
{
_forward[key] = value;
_ = _reverse.Remove(origin);
} else
{
_forward.Add(key, value);
}
_reverse.Add(value, key);
}
}
public bool IsReadOnly => false;
public ICollection<TFirst> FirstIndex => _forward.Keys;
public ICollection<TSecond> SecondIndex => _reverse.Keys;
public int Count => _forward.Count;
public IReadOnlyDictionary<TFirst, TSecond> Forward => new ReadOnlyDictionary<TFirst, TSecond>(
_forward);
public IReadOnlyDictionary<TSecond, TFirst> Reverse => new ReadOnlyDictionary<TSecond, TFirst>(
_reverse);
public void Add(KeyValuePair<TFirst, TSecond> item)
{
if (_forward.ContainsKey(item.Key) || _reverse.ContainsKey(item.Value)) {
throw new ArgumentException("Duplicate key or value.");
}
_forward.Add(item.Key, item.Value);
_reverse.Add(item.Value, item.Key);
}
public void Add(TFirst key, TSecond value)
{
if (_forward.ContainsKey(key) || _reverse.ContainsKey(value)) {
throw new ArgumentException("Duplicate key or value.");
}
_forward.Add(key, value);
_reverse.Add(value, key);
}
public void Clear()
{
_forward.Clear();
_reverse.Clear();
}
public bool Contains(KeyValuePair<TFirst, TSecond> item)
{
return _forward.Contains(item) && _reverse.TryGetValue(item.Value, out TFirst var) && var!.Equals(item.Key);
}
public bool Contains(TSecond key)
{
return _reverse.ContainsKey(key);
}
public bool Contains(TFirst key)
{
return _forward.ContainsKey(key);
}
public void CopyTo(KeyValuePair<TFirst, TSecond>[] array, int arrayIndex)
{
if (array == null) {
throw new ArgumentNullException(nameof(array));
}
if (arrayIndex < 0 || arrayIndex > array.Length) {
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
}
if (array.Length - arrayIndex < _forward.Count) {
throw new ArgumentException("The array is too small to copy the elements.");
}
foreach (KeyValuePair<TFirst, TSecond> pair in _forward) {
array[arrayIndex++] = pair;
}
}
public IEnumerator<KeyValuePair<TFirst, TSecond>> GetEnumerator()
{
foreach (KeyValuePair<TFirst, TSecond> pair in _forward) {
yield return pair;
}
}
public bool Remove(TFirst key)
{
throw new NotImplementedException();
}
public bool Remove(KeyValuePair<TFirst, TSecond> item)
{
if (_forward.Remove(item.Key)) {
return _reverse.Remove(item.Value);
}
return false;
}
public bool TryGetValue(TFirst key, out TSecond value)
{
return _forward.TryGetValue(key, out value);
}
public bool TryGetValue(TSecond key, out TFirst value)
{
return _reverse.TryGetValue(key, out value);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="BidirectionalDictionary"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace StgSharp.Collections
{
public sealed class BidirectionalDictionary<TFirst, TSecond> : IBidirectionalDictionary<TFirst, TSecond>
where TFirst: notnull
where TSecond: notnull
{
private readonly Dictionary<TFirst, TSecond> _forward;
private readonly Dictionary<TSecond, TFirst> _reverse;
public BidirectionalDictionary()
{
_forward = [];
_reverse = [];
}
public TFirst this[TSecond key]
{
get => _reverse.TryGetValue(key, out TFirst value) ?
value :
throw new ArgumentException($"Cannot find key {key} in dictionary.");
set
{
if (_reverse.TryGetValue(key, out TFirst origin))
{
_reverse[key] = value;
_ = _forward.Remove(origin);
} else
{
_reverse.Add(key, value);
}
_forward.Add(value, key);
}
}
public TSecond this[TFirst key]
{
get => _forward.TryGetValue(key, out TSecond value) ?
value :
throw new ArgumentException($"Cannot find key {key} in dictionary.");
set
{
if (_forward.TryGetValue(key, out TSecond origin))
{
_forward[key] = value;
_ = _reverse.Remove(origin);
} else
{
_forward.Add(key, value);
}
_reverse.Add(value, key);
}
}
public bool IsReadOnly => false;
public ICollection<TFirst> FirstIndex => _forward.Keys;
public ICollection<TSecond> SecondIndex => _reverse.Keys;
public int Count => _forward.Count;
public IReadOnlyDictionary<TFirst, TSecond> Forward => new ReadOnlyDictionary<TFirst, TSecond>(
_forward);
public IReadOnlyDictionary<TSecond, TFirst> Reverse => new ReadOnlyDictionary<TSecond, TFirst>(
_reverse);
public void Add(KeyValuePair<TFirst, TSecond> item)
{
if (_forward.ContainsKey(item.Key) || _reverse.ContainsKey(item.Value)) {
throw new ArgumentException("Duplicate key or value.");
}
_forward.Add(item.Key, item.Value);
_reverse.Add(item.Value, item.Key);
}
public void Add(TFirst key, TSecond value)
{
if (_forward.ContainsKey(key) || _reverse.ContainsKey(value)) {
throw new ArgumentException("Duplicate key or value.");
}
_forward.Add(key, value);
_reverse.Add(value, key);
}
public void Clear()
{
_forward.Clear();
_reverse.Clear();
}
public bool Contains(KeyValuePair<TFirst, TSecond> item)
{
return _forward.Contains(item) && _reverse.TryGetValue(item.Value, out TFirst var) && var!.Equals(item.Key);
}
public bool Contains(TSecond key)
{
return _reverse.ContainsKey(key);
}
public bool Contains(TFirst key)
{
return _forward.ContainsKey(key);
}
public void CopyTo(KeyValuePair<TFirst, TSecond>[] array, int arrayIndex)
{
if (array == null) {
throw new ArgumentNullException(nameof(array));
}
if (arrayIndex < 0 || arrayIndex > array.Length) {
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
}
if (array.Length - arrayIndex < _forward.Count) {
throw new ArgumentException("The array is too small to copy the elements.");
}
foreach (KeyValuePair<TFirst, TSecond> pair in _forward) {
array[arrayIndex++] = pair;
}
}
public IEnumerator<KeyValuePair<TFirst, TSecond>> GetEnumerator()
{
foreach (KeyValuePair<TFirst, TSecond> pair in _forward) {
yield return pair;
}
}
public bool Remove(TFirst key)
{
throw new NotImplementedException();
}
public bool Remove(KeyValuePair<TFirst, TSecond> item)
{
if (_forward.Remove(item.Key)) {
return _reverse.Remove(item.Value);
}
return false;
}
public bool TryGetValue(TFirst key, out TSecond value)
{
return _forward.TryGetValue(key, out value);
}
public bool TryGetValue(TSecond key, out TFirst value)
{
return _reverse.TryGetValue(key, out value);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
+92 -92
View File
@@ -1,92 +1,92 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="CapacityFixedBag"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
namespace StgSharp.Collections
{
public sealed class CapacityFixedBag<T> : IEnumerable<T>
{
private readonly T[] _buffer;
public CapacityFixedBag(int capacity)
{
_buffer = new T[capacity];
}
public int Count { get; set; }
public int Capacity => _buffer.Length;
public void Add(T item)
{
if (Count >= _buffer.Length) {
throw new InvalidOperationException("Bag capacity exceeded.");
}
_buffer[Count] = item;
Count++;
}
public IEnumerator<T> GetEnumerator()
{
for (int i = 0; i < Count; i++) {
yield return _buffer[i];
}
}
public void Remove(T item)
{
int index = Array.IndexOf(_buffer, item, 0, Count);
if (index >= 0)
{
_buffer[index] = _buffer[Count - 1];
_buffer[Count - 1] = default!;
Count--;
}
}
public void RemoveAt(int index)
{
if (index >= 0)
{
_buffer[index] = _buffer[Count - 1];
_buffer[Count - 1] = default!;
Count--;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="CapacityFixedBag"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
namespace StgSharp.Collections
{
public sealed class CapacityFixedBag<T> : IEnumerable<T>
{
private readonly T[] _buffer;
public CapacityFixedBag(int capacity)
{
_buffer = new T[capacity];
}
public int Count { get; set; }
public int Capacity => _buffer.Length;
public void Add(T item)
{
if (Count >= _buffer.Length) {
throw new InvalidOperationException("Bag capacity exceeded.");
}
_buffer[Count] = item;
Count++;
}
public IEnumerator<T> GetEnumerator()
{
for (int i = 0; i < Count; i++) {
yield return _buffer[i];
}
}
public void Remove(T item)
{
int index = Array.IndexOf(_buffer, item, 0, Count);
if (index >= 0)
{
_buffer[index] = _buffer[Count - 1];
_buffer[Count - 1] = default!;
Count--;
}
}
public void RemoveAt(int index)
{
if (index >= 0)
{
_buffer[index] = _buffer[Count - 1];
_buffer[Count - 1] = default!;
Count--;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
+176 -176
View File
@@ -1,176 +1,176 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="CapacityFixedList"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace StgSharp.Collections
{
public static class CapacityFixedListBuilder
{
internal static CapacityFixedList<T> Create<T>(ReadOnlySpan<T> values)
{
return new CapacityFixedList<T>(values);
}
}
[CollectionBuilder(typeof(CapacityFixedListBuilder), nameof(CapacityFixedListBuilder.Create))]
public class CapacityFixedList<T> : IList<T>
{
private T[] _values;
private readonly int _capacity;
private int _index;
public CapacityFixedList(int size)
{
_values = new T[size];
_capacity = size;
}
public CapacityFixedList(ReadOnlySpan<T> values)
{
_values = values.ToArray();
}
public T this[int index]
{
get => index < _index ? _values[index] : throw new ArgumentOutOfRangeException(nameof(index));
set => _values[index] = index < _index ? value : throw new ArgumentOutOfRangeException(nameof(index));
}
public bool IsReadOnly => false;
public int Count => _index + 1;
public void Add(T item)
{
if (_index < _capacity - 1)
{
_index++;
this[_index] = item;
}
}
public void Clear()
{
_values = new T[_capacity];
_index = 0;
}
public bool Contains(T item)
{
for (int i = 0; i <= _index; i++)
{
if (item.Equals(_values[i])) {
return true;
}
}
return false;
}
public void CopyTo(T[] array, int arrayIndex)
{
_values.CopyTo(array, arrayIndex);
}
public IEnumerator<T> GetEnumerator()
{
for (int i = 0; i <= _index; i++) {
yield return _values[i];
}
}
public int IndexOf(T item)
{
T v;
for (int i = 0; i <= _index; i++)
{
if ((v = _values[i]) != null && v.Equals(item)) {
return i;
}
}
return -1;
}
public bool Remove(T item)
{
T v;
for (int i = 0; i <= _index; i++)
{
if ((v = _values[i]) != null && v.Equals(item))
{
_values[i] = _values[_index];
_index--;
return true;
}
}
return false;
}
public void RemoveAll(Predicate<T> predicate)
{
for (int i = _index; i > 0; i--)
{
if (predicate(_values[i]))
{
_values[i] = _values[_index];
_index--;
}
}
}
public void RemoveAt(int index)
{
if (index <= _index)
{
_values[index] = _values[_index];
_index--;
}
}
public void Resize(int newSize)
{
Array.Resize(ref _values, newSize);
}
IEnumerator IEnumerable.GetEnumerator()
{
yield return GetEnumerator();
}
void IList<T>.Insert(int index, T item)
{
throw new NotSupportedException();
}
}//------------------------------------- End of Class ------------------------------------------
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="CapacityFixedList"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace StgSharp.Collections
{
public static class CapacityFixedListBuilder
{
internal static CapacityFixedList<T> Create<T>(ReadOnlySpan<T> values)
{
return new CapacityFixedList<T>(values);
}
}
[CollectionBuilder(typeof(CapacityFixedListBuilder), nameof(CapacityFixedListBuilder.Create))]
public class CapacityFixedList<T> : IList<T>
{
private T[] _values;
private readonly int _capacity;
private int _index;
public CapacityFixedList(int size)
{
_values = new T[size];
_capacity = size;
}
public CapacityFixedList(ReadOnlySpan<T> values)
{
_values = values.ToArray();
}
public T this[int index]
{
get => index < _index ? _values[index] : throw new ArgumentOutOfRangeException(nameof(index));
set => _values[index] = index < _index ? value : throw new ArgumentOutOfRangeException(nameof(index));
}
public bool IsReadOnly => false;
public int Count => _index + 1;
public void Add(T item)
{
if (_index < _capacity - 1)
{
_index++;
this[_index] = item;
}
}
public void Clear()
{
_values = new T[_capacity];
_index = 0;
}
public bool Contains(T item)
{
for (int i = 0; i <= _index; i++)
{
if (item.Equals(_values[i])) {
return true;
}
}
return false;
}
public void CopyTo(T[] array, int arrayIndex)
{
_values.CopyTo(array, arrayIndex);
}
public IEnumerator<T> GetEnumerator()
{
for (int i = 0; i <= _index; i++) {
yield return _values[i];
}
}
public int IndexOf(T item)
{
T v;
for (int i = 0; i <= _index; i++)
{
if ((v = _values[i]) != null && v.Equals(item)) {
return i;
}
}
return -1;
}
public bool Remove(T item)
{
T v;
for (int i = 0; i <= _index; i++)
{
if ((v = _values[i]) != null && v.Equals(item))
{
_values[i] = _values[_index];
_index--;
return true;
}
}
return false;
}
public void RemoveAll(Predicate<T> predicate)
{
for (int i = _index; i > 0; i--)
{
if (predicate(_values[i]))
{
_values[i] = _values[_index];
_index--;
}
}
}
public void RemoveAt(int index)
{
if (index <= _index)
{
_values[index] = _values[_index];
_index--;
}
}
public void Resize(int newSize)
{
Array.Resize(ref _values, newSize);
}
IEnumerator IEnumerable.GetEnumerator()
{
yield return GetEnumerator();
}
void IList<T>.Insert(int index, T item)
{
throw new NotSupportedException();
}
}//------------------------------------- End of Class ------------------------------------------
}
+173 -173
View File
@@ -1,174 +1,174 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="CapacityFixedStack"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
namespace StgSharp.Collections
{
[CollectionBuilder(typeof(CapacityFixedStackBuilder), nameof(CapacityFixedStackBuilder.Create))]
public class CapacityFixedStack<T> : IEnumerable<T>
{
private readonly T[] _values;
// _index points to the next free slot (top empty position). Therefore Count == _index.
private int _index;
public CapacityFixedStack(int size)
{
ArgumentOutOfRangeException.ThrowIfNegative(size);
_values = new T[size];
_index = 0; // empty stack
}
public T? First => Volatile.Read(ref _index) > 0 ? _values[0] : default;
public bool IsEmpty => Volatile.Read(ref _index) == 0;
public int Count => Volatile.Read(ref _index);
public int Capacity => _values.Length;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> AsSpan()
{
return _values.AsSpan(0, _index);
}
public void Clear()
{
if (_index > 0)
{
Array.Clear(_values, 0, _index);
_index = 0;
}
}
public IEnumerator<T> GetEnumerator()
{
for (int i = _index - 1; i >= 0; i--) {
yield return _values[i];
}
}
public T Peek()
{
if (_index == 0) {
throw new InvalidOperationException("Stack is empty.");
}
return _values[_index - 1];
}
public T Pop()
{
if (_index == 0) {
throw new InvalidOperationException("Stack is empty.");
}
_index--;
T item = _values[_index];
_values[_index] = default!; // Clear reference for GC
return item;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] PopRange(int count)
{
if (count <= 0 || _index == 0) {
return Array.Empty<T>();
}
int n = Math.Min(count, _index);
int start = _index - n;
T[] ret = new T[n];
Array.Copy(_values, start, ret, 0, n);
Array.Clear(_values, start, n);
_index -= n;
return ret;
}
public void Push(T item)
{
if (_index >= _values.Length) {
throw new InvalidOperationException("Stack is full.");
}
_values[_index] = item;
_index++;
}
public void PushRange(ReadOnlySpan<T> span)
{
if (span.Length == 0) {
return;
}
if (_index + span.Length > _values.Length) {
throw new InvalidOperationException("Not enough space in the stack to push the entire span.");
}
span.CopyTo(_values.AsSpan(_index, span.Length));
_index += span.Length;
}
public bool TryPop(out T value)
{
if (_index == 0)
{
value = default!;
return false;
}
_index--;
value = _values[_index];
_values[_index] = default!;
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Span<T> GetValueLayout()
{
return _values.AsSpan();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public static class CapacityFixedStackBuilder
{
public static CapacityFixedStack<T> Create<T>(ReadOnlySpan<T> values)
{
CapacityFixedStack<T> stack = new(values.Length);
stack.PushRange(values); // Push in order
return stack;
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="CapacityFixedStack"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
namespace StgSharp.Collections
{
[CollectionBuilder(typeof(CapacityFixedStackBuilder), nameof(CapacityFixedStackBuilder.Create))]
public class CapacityFixedStack<T> : IEnumerable<T>
{
private readonly T[] _values;
// _index points to the next free slot (top empty position). Therefore Count == _index.
private int _index;
public CapacityFixedStack(int size)
{
ArgumentOutOfRangeException.ThrowIfNegative(size);
_values = new T[size];
_index = 0; // empty stack
}
public T? First => Volatile.Read(ref _index) > 0 ? _values[0] : default;
public bool IsEmpty => Volatile.Read(ref _index) == 0;
public int Count => Volatile.Read(ref _index);
public int Capacity => _values.Length;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> AsSpan()
{
return _values.AsSpan(0, _index);
}
public void Clear()
{
if (_index > 0)
{
Array.Clear(_values, 0, _index);
_index = 0;
}
}
public IEnumerator<T> GetEnumerator()
{
for (int i = _index - 1; i >= 0; i--) {
yield return _values[i];
}
}
public T Peek()
{
if (_index == 0) {
throw new InvalidOperationException("Stack is empty.");
}
return _values[_index - 1];
}
public T Pop()
{
if (_index == 0) {
throw new InvalidOperationException("Stack is empty.");
}
_index--;
T item = _values[_index];
_values[_index] = default!; // Clear reference for GC
return item;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] PopRange(int count)
{
if (count <= 0 || _index == 0) {
return Array.Empty<T>();
}
int n = Math.Min(count, _index);
int start = _index - n;
T[] ret = new T[n];
Array.Copy(_values, start, ret, 0, n);
Array.Clear(_values, start, n);
_index -= n;
return ret;
}
public void Push(T item)
{
if (_index >= _values.Length) {
throw new InvalidOperationException("Stack is full.");
}
_values[_index] = item;
_index++;
}
public void PushRange(ReadOnlySpan<T> span)
{
if (span.Length == 0) {
return;
}
if (_index + span.Length > _values.Length) {
throw new InvalidOperationException("Not enough space in the stack to push the entire span.");
}
span.CopyTo(_values.AsSpan(_index, span.Length));
_index += span.Length;
}
public bool TryPop(out T value)
{
if (_index == 0)
{
value = default!;
return false;
}
_index--;
value = _values[_index];
_values[_index] = default!;
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Span<T> GetValueLayout()
{
return _values.AsSpan();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public static class CapacityFixedStackBuilder
{
public static CapacityFixedStack<T> Create<T>(ReadOnlySpan<T> values)
{
CapacityFixedStack<T> stack = new(values.Length);
stack.PushRange(values); // Push in order
return stack;
}
}
}
@@ -1,287 +1,287 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="ConcurrentCapacityFixedStack"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
namespace StgSharp.Collections
{
/// <summary>
/// Lock-free, fixed-capacity stack implemented on top of an array using atomic index
/// operations. - No linked list; only array slots - Uses CAS on a shared top index to
/// reserve/release slots - Per-slot state flag to safely publish/consume values under races
/// </summary>
public class ConcurrentCapacityFixedStack<T> : IEnumerable<T>
{
private readonly int[] _states; // 0 = empty, 1 = full
private readonly T[] _values;
private readonly int _capacity;
// Number of full slots (observable count)
private int _count;
// Next push position (0.._capacity). Only modified via CAS.
private int _top;
public ConcurrentCapacityFixedStack(int capacity)
{
if (capacity <= 0) {
throw new ArgumentOutOfRangeException(nameof(capacity));
}
_capacity = capacity;
_values = new T[capacity];
_states = new int[capacity];
_top = 0;
_count = 0;
}
public bool IsEmpty => Count == 0;
public int Capacity => _capacity;
public int Count => Volatile.Read(ref _count);
public void Clear()
{
// Pop all elements to return slots to empty state
while (TryPop(out _)) { }
}
public IEnumerator<T> GetEnumerator()
{
T[] snap = ToArraySnapshot();
for (int i = 0; i < snap.Length; i++) {
yield return snap[i];
}
}
public T Peek()
{
if (TryPeek(out T? v)) {
return v;
}
throw new InvalidOperationException("Stack is empty.");
}
public T Pop()
{
if (TryPop(out T value)) {
return value;
}
throw new InvalidOperationException("Stack is empty.");
}
public T[] PopRange(int count)
{
if (count <= 0) {
return [];
}
T[] result = new T[count];
int i = 0;
for (; i < count; i++)
{
if (!TryPop(out result[i]))
{
break;
}
}
if (i == count) {
return result;
}
if (i == 0) {
return [];
}
T[] trimmed = new T[i];
Array.Copy(result, trimmed, i);
return trimmed;
}
public void Push(T item)
{
SpinWait sw = default;
while (true)
{
int oldTop = Volatile.Read(ref _top);
if (oldTop >= _capacity) {
throw new InvalidOperationException("Stack is full.");
}
if (Interlocked.CompareExchange(ref _top, oldTop + 1, oldTop) != oldTop)
{
sw.SpinOnce();
continue; // failed to reserve slot
}
// We exclusively own slot oldTop. Publish value then state.
_values[oldTop] = item;
Volatile.Write(ref _states[oldTop], 1); // publish
_ = Interlocked.Increment(ref _count);
return;
}
}
public void PushRange(ReadOnlySpan<T> span)
{
for (int i = 0; i < span.Length; i++) {
Push(span[i]);
}
}
/// <summary>
/// Best-effort snapshot for enumeration; not strongly consistent under concurrent
/// mutations.
/// </summary>
public T[] ToArraySnapshot()
{
int expected = Math.Min(Volatile.Read(ref _count), _capacity);
if (expected == 0) {
return Array.Empty<T>();
}
T[] snap = new T[expected];
int copied = 0;
int idx = Volatile.Read(ref _top) - 1;
while (idx >= 0 && copied < expected)
{
if (Volatile.Read(ref _states[idx]) == 1) {
snap[copied++] = _values[idx];
}
idx--;
}
if (copied == snap.Length) {
return snap;
}
T[] trimmed = new T[copied];
Array.Copy(snap, trimmed, copied);
return trimmed;
}
public bool TryPeek(out T value)
{
value = default!;
SpinWait sw = default;
while (true)
{
int top = Volatile.Read(ref _top);
if (top <= 0) {
return false;
}
int idx = top - 1;
// If not yet published or racing, retry/spin
if (Volatile.Read(ref _states[idx]) == 0)
{
if (Volatile.Read(ref _top) != top)
{
continue; // head moved, retry
}
sw.SpinOnce();
continue;
}
T v = _values[idx];
// Validate consistency
if (Volatile.Read(ref _top) == top && Volatile.Read(ref _states[idx]) == 1)
{
value = v;
return true;
}
}
}
public bool TryPop(out T value)
{
value = default!;
SpinWait sw = default;
while (true)
{
int oldTop = Volatile.Read(ref _top);
if (oldTop <= 0)
{
return false; // empty
}
int newTop = oldTop - 1; // index to consume
if (Interlocked.CompareExchange(ref _top, newTop, oldTop) != oldTop)
{
sw.SpinOnce();
continue; // failed to reserve element
}
// If producer hasn't published yet, roll back our reservation and retry.
if (Volatile.Read(ref _states[newTop]) == 0)
{
// attempt to restore top; ok if it fails due to concurrent operations
_ = Interlocked.CompareExchange(ref _top, newTop + 1, newTop);
sw.SpinOnce();
continue;
}
value = _values[newTop];
_values[newTop] = default!; // avoid ref retention
Volatile.Write(ref _states[newTop], 0); // mark empty
_ = Interlocked.Decrement(ref _count);
return true;
}
}
public int TryPopRange(Span<T> destination)
{
if (destination.IsEmpty) {
return 0;
}
int i = 0;
for (; i < destination.Length; i++)
{
if (!TryPop(out destination[i]))
{
break;
}
}
return i;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="ConcurrentCapacityFixedStack"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
namespace StgSharp.Collections
{
/// <summary>
/// Lock-free, fixed-capacity stack implemented on top of an array using atomic index
/// operations. - No linked list; only array slots - Uses CAS on a shared top index to
/// reserve/release slots - Per-slot state flag to safely publish/consume values under races
/// </summary>
public class ConcurrentCapacityFixedStack<T> : IEnumerable<T>
{
private readonly int[] _states; // 0 = empty, 1 = full
private readonly T[] _values;
private readonly int _capacity;
// Number of full slots (observable count)
private int _count;
// Next push position (0.._capacity). Only modified via CAS.
private int _top;
public ConcurrentCapacityFixedStack(int capacity)
{
if (capacity <= 0) {
throw new ArgumentOutOfRangeException(nameof(capacity));
}
_capacity = capacity;
_values = new T[capacity];
_states = new int[capacity];
_top = 0;
_count = 0;
}
public bool IsEmpty => Count == 0;
public int Capacity => _capacity;
public int Count => Volatile.Read(ref _count);
public void Clear()
{
// Pop all elements to return slots to empty state
while (TryPop(out _)) { }
}
public IEnumerator<T> GetEnumerator()
{
T[] snap = ToArraySnapshot();
for (int i = 0; i < snap.Length; i++) {
yield return snap[i];
}
}
public T Peek()
{
if (TryPeek(out T? v)) {
return v;
}
throw new InvalidOperationException("Stack is empty.");
}
public T Pop()
{
if (TryPop(out T value)) {
return value;
}
throw new InvalidOperationException("Stack is empty.");
}
public T[] PopRange(int count)
{
if (count <= 0) {
return [];
}
T[] result = new T[count];
int i = 0;
for (; i < count; i++)
{
if (!TryPop(out result[i]))
{
break;
}
}
if (i == count) {
return result;
}
if (i == 0) {
return [];
}
T[] trimmed = new T[i];
Array.Copy(result, trimmed, i);
return trimmed;
}
public void Push(T item)
{
SpinWait sw = default;
while (true)
{
int oldTop = Volatile.Read(ref _top);
if (oldTop >= _capacity) {
throw new InvalidOperationException("Stack is full.");
}
if (Interlocked.CompareExchange(ref _top, oldTop + 1, oldTop) != oldTop)
{
sw.SpinOnce();
continue; // failed to reserve slot
}
// We exclusively own slot oldTop. Publish value then state.
_values[oldTop] = item;
Volatile.Write(ref _states[oldTop], 1); // publish
_ = Interlocked.Increment(ref _count);
return;
}
}
public void PushRange(ReadOnlySpan<T> span)
{
for (int i = 0; i < span.Length; i++) {
Push(span[i]);
}
}
/// <summary>
/// Best-effort snapshot for enumeration; not strongly consistent under concurrent
/// mutations.
/// </summary>
public T[] ToArraySnapshot()
{
int expected = Math.Min(Volatile.Read(ref _count), _capacity);
if (expected == 0) {
return Array.Empty<T>();
}
T[] snap = new T[expected];
int copied = 0;
int idx = Volatile.Read(ref _top) - 1;
while (idx >= 0 && copied < expected)
{
if (Volatile.Read(ref _states[idx]) == 1) {
snap[copied++] = _values[idx];
}
idx--;
}
if (copied == snap.Length) {
return snap;
}
T[] trimmed = new T[copied];
Array.Copy(snap, trimmed, copied);
return trimmed;
}
public bool TryPeek(out T value)
{
value = default!;
SpinWait sw = default;
while (true)
{
int top = Volatile.Read(ref _top);
if (top <= 0) {
return false;
}
int idx = top - 1;
// If not yet published or racing, retry/spin
if (Volatile.Read(ref _states[idx]) == 0)
{
if (Volatile.Read(ref _top) != top)
{
continue; // head moved, retry
}
sw.SpinOnce();
continue;
}
T v = _values[idx];
// Validate consistency
if (Volatile.Read(ref _top) == top && Volatile.Read(ref _states[idx]) == 1)
{
value = v;
return true;
}
}
}
public bool TryPop(out T value)
{
value = default!;
SpinWait sw = default;
while (true)
{
int oldTop = Volatile.Read(ref _top);
if (oldTop <= 0)
{
return false; // empty
}
int newTop = oldTop - 1; // index to consume
if (Interlocked.CompareExchange(ref _top, newTop, oldTop) != oldTop)
{
sw.SpinOnce();
continue; // failed to reserve element
}
// If producer hasn't published yet, roll back our reservation and retry.
if (Volatile.Read(ref _states[newTop]) == 0)
{
// attempt to restore top; ok if it fails due to concurrent operations
_ = Interlocked.CompareExchange(ref _top, newTop + 1, newTop);
sw.SpinOnce();
continue;
}
value = _values[newTop];
_values[newTop] = default!; // avoid ref retention
Volatile.Write(ref _states[newTop], 0); // mark empty
_ = Interlocked.Decrement(ref _count);
return true;
}
}
public int TryPopRange(Span<T> destination)
{
if (destination.IsEmpty) {
return 0;
}
int i = 0;
for (; i < destination.Length; i++)
{
if (!TryPop(out destination[i]))
{
break;
}
}
return i;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
@@ -1,84 +1,84 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="IBidirectionalDictionary"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System.Collections.Generic;
namespace StgSharp.Collections
{
public interface IBidirectionalDictionary<TFirst, TSecond> : IDictionary<TFirst, TSecond>
{
TFirst this[TSecond key] { get; set; }
ICollection<TFirst> FirstIndex { get; }
ICollection<TSecond> SecondIndex { get; }
IReadOnlyDictionary<TFirst, TSecond> Forward { get; }
IReadOnlyDictionary<TSecond, TFirst> Reverse { get; }
/// <summary>
/// Determines whether the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains the
/// specified key.
/// </summary>
/// <param _label="key">
/// The value to locate in the <see cref="IBidirectionalDictionary{TFirst, TSecond}" />.
/// </param>
/// <returns>
/// true if the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains an element with
/// the specified key; otherwise, false.
/// </returns>
bool Contains(TSecond key);
/// <summary>
/// Determines whether the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains the
/// specified key.
/// </summary>
/// <param _label="key">
/// The value to locate in the <see cref="IBidirectionalDictionary{TFirst, TSecond}" />.
/// </param>
/// <returns>
/// true if the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains an element with
/// the specified key; otherwise, false.
/// </returns>
bool Contains(TFirst key);
bool TryGetValue(TSecond key, out TFirst value);
#pragma warning disable CA1033
ICollection<TFirst> IDictionary<TFirst, TSecond>.Keys => FirstIndex;
ICollection<TSecond> IDictionary<TFirst, TSecond>.Values => SecondIndex;
bool IDictionary<TFirst, TSecond>.ContainsKey(TFirst key)
{
return Contains(key);
}
#pragma warning restore CA1033
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="IBidirectionalDictionary"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System.Collections.Generic;
namespace StgSharp.Collections
{
public interface IBidirectionalDictionary<TFirst, TSecond> : IDictionary<TFirst, TSecond>
{
TFirst this[TSecond key] { get; set; }
ICollection<TFirst> FirstIndex { get; }
ICollection<TSecond> SecondIndex { get; }
IReadOnlyDictionary<TFirst, TSecond> Forward { get; }
IReadOnlyDictionary<TSecond, TFirst> Reverse { get; }
/// <summary>
/// Determines whether the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains the
/// specified key.
/// </summary>
/// <param _label="key">
/// The value to locate in the <see cref="IBidirectionalDictionary{TFirst, TSecond}" />.
/// </param>
/// <returns>
/// true if the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains an element with
/// the specified key; otherwise, false.
/// </returns>
bool Contains(TSecond key);
/// <summary>
/// Determines whether the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains the
/// specified key.
/// </summary>
/// <param _label="key">
/// The value to locate in the <see cref="IBidirectionalDictionary{TFirst, TSecond}" />.
/// </param>
/// <returns>
/// true if the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains an element with
/// the specified key; otherwise, false.
/// </returns>
bool Contains(TFirst key);
bool TryGetValue(TSecond key, out TFirst value);
#pragma warning disable CA1033
ICollection<TFirst> IDictionary<TFirst, TSecond>.Keys => FirstIndex;
ICollection<TSecond> IDictionary<TFirst, TSecond>.Values => SecondIndex;
bool IDictionary<TFirst, TSecond>.ContainsKey(TFirst key)
{
return Contains(key);
}
#pragma warning restore CA1033
}
}
+52 -52
View File
@@ -1,53 +1,53 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="IHashMultiplexer"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System.Collections.Generic;
namespace StgSharp.Collections
{
public interface IHashMultiplexer<T> : IEqualityComparer<T>
{
int RemainedMultiplexCount { get; set; }
/// <summary>
/// Cache hash result for obj with counts.
/// </summary>
/// <param name="obj">
///
/// </param>
/// <param name="count">
///
/// </param>
void CacheHash(T obj, int count);
void ExtendCache(int count);
int ForceComputeHash(T count);
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="IHashMultiplexer"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System.Collections.Generic;
namespace StgSharp.Collections
{
public interface IHashMultiplexer<T> : IEqualityComparer<T>
{
int RemainedMultiplexCount { get; set; }
/// <summary>
/// Cache hash result for obj with counts.
/// </summary>
/// <param name="obj">
///
/// </param>
/// <param name="count">
///
/// </param>
void CacheHash(T obj, int count);
void ExtendCache(int count);
int ForceComputeHash(T count);
}
}
+131 -131
View File
@@ -1,131 +1,131 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="ListSegment"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace StgSharp.Collections
{
/// <summary>
/// Part of a collection. This contains a direct reference to _origin collection, so all
/// change to _origin collection will be reflected in this object.
/// </summary>
/// <typeparam name="T">
///
/// </typeparam>
public struct ListSegment<T> : ICollection<T>
{
private int _begin;
private int _length;
private List<T> _collection;
public ListSegment(List<T> collection, int begin, int length)
{
ArgumentNullException.ThrowIfNull(collection);
ArgumentOutOfRangeException.ThrowIfLessThan(begin, 0);
ArgumentOutOfRangeException.ThrowIfLessThan(length, 0);
ArgumentOutOfRangeException.ThrowIfLessThan(begin + length, collection.Count);
_collection = collection;
_begin = begin;
_length = length;
}
public T this[int indexValue]
{
get
{
ArgumentOutOfRangeException.ThrowIfLessThan(indexValue, 0);
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(indexValue, _length);
return _collection[indexValue + _begin];
}
}
public T this[Index i]
{
get
{
int offset = i.IsFromEnd ? _length - i.Value : i.Value;
ArgumentOutOfRangeException.ThrowIfLessThan(offset, 0);
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(offset, _length);
return _collection[offset + _begin];
}
}
public bool IsReadOnly => true;
public int Count => _length;
public bool Contains(T item)
{
int index = _collection.IndexOf(item);
return index >= _begin && index < _length + index;
}
public void CopyTo(T[] array, int arrayIndex)
{
ArgumentNullException.ThrowIfNull(array);
Span<T> span = CollectionsMarshal.AsSpan<T>(_collection).Slice(_begin, _length);
Span<T> target = new Span<T>(array, arrayIndex, _length);
span.CopyTo(target);
}
public IEnumerator<T> GetEnumerator()
{
for (int i = _begin; i < _length; i++) {
yield return this[i];
}
}
void ICollection<T>.Add(T item)
{
throw new NotSupportedException();
}
void ICollection<T>.Clear()
{
throw new NotSupportedException();
}
bool ICollection<T>.Remove(T item)
{
throw new NotSupportedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="ListSegment"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace StgSharp.Collections
{
/// <summary>
/// Part of a collection. This contains a direct reference to _origin collection, so all
/// change to _origin collection will be reflected in this object.
/// </summary>
/// <typeparam name="T">
///
/// </typeparam>
public struct ListSegment<T> : ICollection<T>
{
private int _begin;
private int _length;
private List<T> _collection;
public ListSegment(List<T> collection, int begin, int length)
{
ArgumentNullException.ThrowIfNull(collection);
ArgumentOutOfRangeException.ThrowIfLessThan(begin, 0);
ArgumentOutOfRangeException.ThrowIfLessThan(length, 0);
ArgumentOutOfRangeException.ThrowIfLessThan(begin + length, collection.Count);
_collection = collection;
_begin = begin;
_length = length;
}
public T this[int indexValue]
{
get
{
ArgumentOutOfRangeException.ThrowIfLessThan(indexValue, 0);
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(indexValue, _length);
return _collection[indexValue + _begin];
}
}
public T this[Index i]
{
get
{
int offset = i.IsFromEnd ? _length - i.Value : i.Value;
ArgumentOutOfRangeException.ThrowIfLessThan(offset, 0);
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(offset, _length);
return _collection[offset + _begin];
}
}
public bool IsReadOnly => true;
public int Count => _length;
public bool Contains(T item)
{
int index = _collection.IndexOf(item);
return index >= _begin && index < _length + index;
}
public void CopyTo(T[] array, int arrayIndex)
{
ArgumentNullException.ThrowIfNull(array);
Span<T> span = CollectionsMarshal.AsSpan<T>(_collection).Slice(_begin, _length);
Span<T> target = new Span<T>(array, arrayIndex, _length);
span.CopyTo(target);
}
public IEnumerator<T> GetEnumerator()
{
for (int i = _begin; i < _length; i++) {
yield return this[i];
}
}
void ICollection<T>.Add(T item)
{
throw new NotSupportedException();
}
void ICollection<T>.Clear()
{
throw new NotSupportedException();
}
bool ICollection<T>.Remove(T item)
{
throw new NotSupportedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
+200 -200
View File
@@ -1,200 +1,200 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="NotEnumerableList.cs"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace StgSharp.Collections
{
public static class LinearBagBuilder
{
/// <summary>
/// Build a <see cref="LinearBag{T}" /> from a read-only span. Capacity will exactly fit
/// the input.
/// </summary>
public static LinearBag<T> FromSpan<T>(ReadOnlySpan<T> values)
{
if (values.IsEmpty) {
return new LinearBag<T>(4);
}
LinearBag<T> bag = new(values.Length);
for (int i = 0; i < values.Length; i++) {
bag.Add(values[i]);
}
return bag;
}
}
/// <summary>
/// A very lightweight contiguous storage structure: unordered, not enumerable by design.
/// Provides O(1) Add and O(1) RemoveAt (by swapping with the tail). Intended for high-
/// performance scenarios where element ordering does not matter. NOT thread-safe.
/// </summary>
/// <typeparam name="T">
/// Element type.
/// </typeparam>
[CollectionBuilder(typeof(LinearBagBuilder), nameof(LinearBagBuilder.FromSpan))]
public class LinearBag<T>(int capacity = 4)
{
// Invariant: valid elements occupy indexes [0, Count-1]. Count is the number of live elements.
private T[] _items = new T[Math.Max(0, capacity)];
/// <summary>
/// Access element by index (0..Count-1). Throws if out of range.
/// </summary>
public T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (uint)index >= (uint)Count ? throw new ArgumentOutOfRangeException(nameof(index)) : _items[index];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
if ((uint)index >= (uint)Count) {
throw new ArgumentOutOfRangeException(nameof(index));
}
_items[index] = value;
}
}
/// <summary>
/// Total allocated slots.
/// </summary>
public int Capacity => _items.Length;
/// <summary>
/// Number of currently stored elements (valid index range 0..Count-1).
/// </summary>
public int Count { get; private set; }
/// <summary>
/// Adds an item (unordered). Expands internal array if needed.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(T item)
{
if (Count == _items.Length) {
EnsureCapacity(Count + 1);
}
_items[Count] = item;
Count++;
}
/// <summary>
/// Returns a span over the live portion of the bag (0..Count-1).
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan()
{
return new Span<T>(_items, 0, Count);
}
/// <summary>
/// Clears all elements and zeroes out references to avoid retention. Count becomes 0.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
Array.Clear(_items, 0, Count);
Count = 0;
}
/// <summary>
/// Fast clear: only resets Count (leaves underlying array unchanged). References are NOT
/// released. Use only when you are certain old references can remain.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FastClear()
{
Count = 0;
}
/// <summary>
/// Removes the first occurrence of value (unordered). Does nothing if not found.
/// </summary>
public void Remove(T value)
{
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
for (int i = 0; i < Count; i++)
{
if (comparer.Equals(_items[i], value))
{
RemoveAt(i);
return;
}
}
}
/// <summary>
/// Removes element at index by swapping the last element into its slot (unless already
/// last). Preserves O(1) removal at cost of ordering.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAt(int index)
{
if ((uint)index >= (uint)Count) {
throw new ArgumentOutOfRangeException(nameof(index));
}
int last = Count - 1;
if (index != last) {
_items[index] = _items[last];
}
_items[last] = default!; // release reference / reset value
Count = last;
}
/// <summary>
/// Ensures capacity is at least <paramref name="min" />. Doubles size when growing.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnsureCapacity(int min)
{
if (_items.Length >= min) {
return;
}
int newCapacity = _items.Length == 0 ? 4 : _items.Length * 2;
if (newCapacity < min) {
newCapacity = min;
}
Array.Resize(ref _items, newCapacity);
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="NotEnumerableList.cs"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace StgSharp.Collections
{
public static class LinearBagBuilder
{
/// <summary>
/// Build a <see cref="LinearBag{T}" /> from a read-only span. Capacity will exactly fit
/// the input.
/// </summary>
public static LinearBag<T> FromSpan<T>(ReadOnlySpan<T> values)
{
if (values.IsEmpty) {
return new LinearBag<T>(4);
}
LinearBag<T> bag = new(values.Length);
for (int i = 0; i < values.Length; i++) {
bag.Add(values[i]);
}
return bag;
}
}
/// <summary>
/// A very lightweight contiguous storage structure: unordered, not enumerable by design.
/// Provides O(1) Add and O(1) RemoveAt (by swapping with the tail). Intended for high-
/// performance scenarios where element ordering does not matter. NOT thread-safe.
/// </summary>
/// <typeparam name="T">
/// Element type.
/// </typeparam>
[CollectionBuilder(typeof(LinearBagBuilder), nameof(LinearBagBuilder.FromSpan))]
public class LinearBag<T>(int capacity = 4)
{
// Invariant: valid elements occupy indexes [0, Count-1]. Count is the number of live elements.
private T[] _items = new T[Math.Max(0, capacity)];
/// <summary>
/// Access element by index (0..Count-1). Throws if out of range.
/// </summary>
public T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (uint)index >= (uint)Count ? throw new ArgumentOutOfRangeException(nameof(index)) : _items[index];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
if ((uint)index >= (uint)Count) {
throw new ArgumentOutOfRangeException(nameof(index));
}
_items[index] = value;
}
}
/// <summary>
/// Total allocated slots.
/// </summary>
public int Capacity => _items.Length;
/// <summary>
/// Number of currently stored elements (valid index range 0..Count-1).
/// </summary>
public int Count { get; private set; }
/// <summary>
/// Adds an item (unordered). Expands internal array if needed.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(T item)
{
if (Count == _items.Length) {
EnsureCapacity(Count + 1);
}
_items[Count] = item;
Count++;
}
/// <summary>
/// Returns a span over the live portion of the bag (0..Count-1).
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan()
{
return new Span<T>(_items, 0, Count);
}
/// <summary>
/// Clears all elements and zeroes out references to avoid retention. Count becomes 0.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
Array.Clear(_items, 0, Count);
Count = 0;
}
/// <summary>
/// Fast clear: only resets Count (leaves underlying array unchanged). References are NOT
/// released. Use only when you are certain old references can remain.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FastClear()
{
Count = 0;
}
/// <summary>
/// Removes the first occurrence of value (unordered). Does nothing if not found.
/// </summary>
public void Remove(T value)
{
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
for (int i = 0; i < Count; i++)
{
if (comparer.Equals(_items[i], value))
{
RemoveAt(i);
return;
}
}
}
/// <summary>
/// Removes element at index by swapping the last element into its slot (unless already
/// last). Preserves O(1) removal at cost of ordering.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAt(int index)
{
if ((uint)index >= (uint)Count) {
throw new ArgumentOutOfRangeException(nameof(index));
}
int last = Count - 1;
if (index != last) {
_items[index] = _items[last];
}
_items[last] = default!; // release reference / reset value
Count = last;
}
/// <summary>
/// Ensures capacity is at least <paramref name="min" />. Doubles size when growing.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnsureCapacity(int min)
{
if (_items.Length >= min) {
return;
}
int newCapacity = _items.Length == 0 ? 4 : _items.Length * 2;
if (newCapacity < min) {
newCapacity = min;
}
Array.Resize(ref _items, newCapacity);
}
}
}
@@ -1,181 +1,181 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="RandomAccessibleStack"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
namespace StgSharp.Collections
{
public class RandomAccessibleStack<T> : IEnumerable<T>
{
private const int DefaultCapacity = 4;
private T[] _array;
private int _size;
private int _version;
/// <summary>
/// Initializes a new instance of the RandomAccessStack class that is empty and has the
/// default initial capacity.
/// </summary>
public RandomAccessibleStack()
{
_array = new T[DefaultCapacity];
}
/// <summary>
/// Initializes a new instance of the RandomAccessStack class that is empty and has the
/// specified initial capacity.
/// </summary>
public RandomAccessibleStack(int capacity)
{
if (capacity < 0) {
throw new ArgumentOutOfRangeException(
nameof(capacity), "Capacity must be non-negative.");
}
_array = new T[capacity];
}
/// <summary>
/// Gets or sets the element at the specified index in the stack (0 = bottom, Count-1 =
/// top).
/// </summary>
public T this[int index]
{
get
{
if (index < 0 || index >= _size) {
throw new ArgumentOutOfRangeException(nameof(index));
}
return _array[index];
}
/*
set
{
if( index < 0 || index >= PrimSize ) {
throw new ArgumentOutOfRangeException( nameof( index ) );
}
_array[ index ] = value;
_version++;
}
/**/
}
/// <summary>
/// Gets the number of elements contained in the stack.
/// </summary>
public int Count => _size;
/// <summary>
/// Removes all objects from the stack.
/// </summary>
public void Clear()
{
Array.Clear(_array, 0, _size);
_size = 0;
_version++;
}
/// <summary>
/// Returns an enumerator that iterates through the stack from top to bottom. Throws if
/// the stack is modified during iteration.
/// </summary>
public IEnumerator<T> GetEnumerator()
{
int expectedVersion = _version;
for (int i = _size - 1; i >= 0; i--)
{
if (expectedVersion != _version) {
throw new InvalidOperationException(
"Collection was modified during enumeration.");
}
yield return _array[i];
}
}
/// <summary>
/// Returns the object at the top of the stack without removing it.
/// </summary>
public T Peek()
{
if (_size == 0) {
throw new InvalidOperationException("Stack is empty.");
}
return _array[_size - 1];
}
/// <summary>
/// Removes and returns the object at the top of the stack.
/// </summary>
public T Pop()
{
if (_size == 0) {
throw new InvalidOperationException("Stack is empty.");
}
T item = _array[--_size];
_array[_size] = default!;
_version++;
return item;
}
/// <summary>
/// Adds an item to the top of the stack.
/// </summary>
public void Push(T item)
{
if (_size == _array.Length) {
EnsureCapacity(_size + 1);
}
_array[_size++] = item;
_version++;
}
private void EnsureCapacity(int min)
{
int newCapacity = _array.Length == 0 ? DefaultCapacity : _array.Length * 2;
if (newCapacity < min) {
newCapacity = min;
}
Array.Resize(ref _array, newCapacity);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="RandomAccessibleStack"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
namespace StgSharp.Collections
{
public class RandomAccessibleStack<T> : IEnumerable<T>
{
private const int DefaultCapacity = 4;
private T[] _array;
private int _size;
private int _version;
/// <summary>
/// Initializes a new instance of the RandomAccessStack class that is empty and has the
/// default initial capacity.
/// </summary>
public RandomAccessibleStack()
{
_array = new T[DefaultCapacity];
}
/// <summary>
/// Initializes a new instance of the RandomAccessStack class that is empty and has the
/// specified initial capacity.
/// </summary>
public RandomAccessibleStack(int capacity)
{
if (capacity < 0) {
throw new ArgumentOutOfRangeException(
nameof(capacity), "Capacity must be non-negative.");
}
_array = new T[capacity];
}
/// <summary>
/// Gets or sets the element at the specified index in the stack (0 = bottom, Count-1 =
/// top).
/// </summary>
public T this[int index]
{
get
{
if (index < 0 || index >= _size) {
throw new ArgumentOutOfRangeException(nameof(index));
}
return _array[index];
}
/*
set
{
if( index < 0 || index >= PrimSize ) {
throw new ArgumentOutOfRangeException( nameof( index ) );
}
_array[ index ] = value;
_version++;
}
/**/
}
/// <summary>
/// Gets the number of elements contained in the stack.
/// </summary>
public int Count => _size;
/// <summary>
/// Removes all objects from the stack.
/// </summary>
public void Clear()
{
Array.Clear(_array, 0, _size);
_size = 0;
_version++;
}
/// <summary>
/// Returns an enumerator that iterates through the stack from top to bottom. Throws if
/// the stack is modified during iteration.
/// </summary>
public IEnumerator<T> GetEnumerator()
{
int expectedVersion = _version;
for (int i = _size - 1; i >= 0; i--)
{
if (expectedVersion != _version) {
throw new InvalidOperationException(
"Collection was modified during enumeration.");
}
yield return _array[i];
}
}
/// <summary>
/// Returns the object at the top of the stack without removing it.
/// </summary>
public T Peek()
{
if (_size == 0) {
throw new InvalidOperationException("Stack is empty.");
}
return _array[_size - 1];
}
/// <summary>
/// Removes and returns the object at the top of the stack.
/// </summary>
public T Pop()
{
if (_size == 0) {
throw new InvalidOperationException("Stack is empty.");
}
T item = _array[--_size];
_array[_size] = default!;
_version++;
return item;
}
/// <summary>
/// Adds an item to the top of the stack.
/// </summary>
public void Push(T item)
{
if (_size == _array.Length) {
EnsureCapacity(_size + 1);
}
_array[_size++] = item;
_version++;
}
private void EnsureCapacity(int min)
{
int newCapacity = _array.Length == 0 ? DefaultCapacity : _array.Length * 2;
if (newCapacity < min) {
newCapacity = min;
}
Array.Resize(ref _array, newCapacity);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
+189 -189
View File
@@ -1,91 +1,91 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SwissTable"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
namespace StgSharp.Collections
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SwissTable"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
namespace StgSharp.Collections
{
/// <summary>
///
/// <summary>
///
/// </summary>
public sealed unsafe partial class SwissTable
{
private const int BucketCount = 1 << EntryMaskWidth;
private const int BucketSize = 16;
private const uint CrcMagic = 0xffff9961; // Magic number for CRC32C, used in hash computation
private const int EntryMaskWidth = 16; // 65536 entries total, 16 per bucket
public sealed unsafe partial class SwissTable
{
private const int BucketCount = 1 << EntryMaskWidth;
private const int BucketSize = 16;
private const uint CrcMagic = 0xffff9961; // Magic number for CRC32C, used in hash computation
private const int EntryMaskWidth = 16; // 65536 entries total, 16 per bucket
public const int MaxCapacity = BucketCount * BucketSize; // 256 entries per bucket, 1024 buckets
private readonly Entry* _entries;
private SwissTable(
Entry* buffer
)
{
_entries = buffer;
}
public static SwissTable Create()
{
Entry* buffer = (Entry*)NativeMemory.AlignedAlloc((nuint)Unsafe.SizeOf<Entry>(), 16);
for (int i = 0; i < BucketCount; i++)
{
// Set all control bytes to 0xFF (empty)
Unsafe.InitBlock(&(*buffer)[i].Control, 0xFF, BucketSize);
}
return new SwissTable(buffer);
}
public bool TryAddOrSet(
nint key,
nint value,
out bool isNewKey
)
{
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
ushort entry = (ushort)(hash & (BucketCount - 1));
Bucket* bucket = &(*_entries)[entry];
return IBucketScanner.Current.TrySet(key, hash, bucket, value, out isNewKey);
}
public bool TryGet(
nint key,
out nint value
)
{
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
ushort entry = (ushort)(hash & (BucketCount - 1));
Bucket* bucket = &(*_entries)[entry];
private readonly Entry* _entries;
private SwissTable(
Entry* buffer
)
{
_entries = buffer;
}
public static SwissTable Create()
{
Entry* buffer = (Entry*)NativeMemory.AlignedAlloc((nuint)Unsafe.SizeOf<Entry>(), 16);
for (int i = 0; i < BucketCount; i++)
{
// Set all control bytes to 0xFF (empty)
Unsafe.InitBlock(&(*buffer)[i].Control, 0xFF, BucketSize);
}
return new SwissTable(buffer);
}
public bool TryAddOrSet(
nint key,
nint value,
out bool isNewKey
)
{
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
ushort entry = (ushort)(hash & (BucketCount - 1));
Bucket* bucket = &(*_entries)[entry];
return IBucketScanner.Current.TrySet(key, hash, bucket, value, out isNewKey);
}
public bool TryGet(
nint key,
out nint value
)
{
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
ushort entry = (ushort)(hash & (BucketCount - 1));
Bucket* bucket = &(*_entries)[entry];
return IBucketScanner.Current.TryFind(key, hash, bucket, out value);
/*
* hash = CRC32 of key
@@ -93,108 +93,108 @@ namespace StgSharp.Collections
* ctrl = 8-15 of hash
* index = rest of hash
* find value of hash
*/
}
public bool TryRemove(
nint key,
out nint value
)
{
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
ushort entry = (ushort)(hash & (BucketCount - 1));
Bucket* bucket = &(*_entries)[entry];
return IBucketScanner.Current.TryRemove(key, hash, bucket, out value);
}
~SwissTable()
{
NativeMemory.AlignedFree(_entries);
}
[InlineArray(BucketCount)]
private struct Entry
{
public Bucket Bucket;
}
private unsafe struct Bucket
{
public BucketControl Control;
public BucketKeyStorage Keys;
public BucketValueStorage Values;
}
private interface IBucketScanner
{
static IBucketScanner Current { get; } = Create();
bool TryFind(
nint key,
uint Hash,
Bucket* bucket,
out nint value
);
unsafe bool TryRemove(
nint key,
uint hash,
Bucket* bucket,
out nint value
);
bool TrySet(
nint key,
uint hash,
Bucket* bucket,
nint value,
out bool isNewKey
);
private static IBucketScanner Create()
{
if (Sse42.IsSupported)
{
return new BucketScannerX86();
} else if (ArmBase.IsSupported)
{
throw new NotSupportedException("ARM architecture is currently not supported for bucket scanning. Please use an x86 platform.");
} else
{
throw new NotSupportedException("No suitable bucket scanner implementation found for the current platform.");
}
}
}
[InlineArray(BucketSize)]
private struct BucketControl
{
public byte Control;
}
[InlineArray(BucketSize)]
private struct BucketKeyStorage
{
public nint value;
}
[InlineArray(BucketSize)]
private struct BucketValueStorage
{
public nint value;
}
}
*/
}
public bool TryRemove(
nint key,
out nint value
)
{
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
ushort entry = (ushort)(hash & (BucketCount - 1));
Bucket* bucket = &(*_entries)[entry];
return IBucketScanner.Current.TryRemove(key, hash, bucket, out value);
}
~SwissTable()
{
NativeMemory.AlignedFree(_entries);
}
[InlineArray(BucketCount)]
private struct Entry
{
public Bucket Bucket;
}
private unsafe struct Bucket
{
public BucketControl Control;
public BucketKeyStorage Keys;
public BucketValueStorage Values;
}
private interface IBucketScanner
{
static IBucketScanner Current { get; } = Create();
bool TryFind(
nint key,
uint Hash,
Bucket* bucket,
out nint value
);
unsafe bool TryRemove(
nint key,
uint hash,
Bucket* bucket,
out nint value
);
bool TrySet(
nint key,
uint hash,
Bucket* bucket,
nint value,
out bool isNewKey
);
private static IBucketScanner Create()
{
if (Sse42.IsSupported)
{
return new BucketScannerX86();
} else if (ArmBase.IsSupported)
{
throw new NotSupportedException("ARM architecture is currently not supported for bucket scanning. Please use an x86 platform.");
} else
{
throw new NotSupportedException("No suitable bucket scanner implementation found for the current platform.");
}
}
}
[InlineArray(BucketSize)]
private struct BucketControl
{
public byte Control;
}
[InlineArray(BucketSize)]
private struct BucketKeyStorage
{
public nint value;
}
[InlineArray(BucketSize)]
private struct BucketValueStorage
{
public nint value;
}
}
}
+190 -190
View File
@@ -1,190 +1,190 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="Dialogue"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace StgSharp
{
public static class Dialogue
{
private static Process? _dialogueProcess;
private static AnonymousPipeServerStream _dialogueClientPipe,_dialogueServerPipe;
private static int _readAndWriteThreadId;
private static ProcessStartInfo _dialogueProcessInfo;
private static StreamReader _dr;
private static StreamWriter _dw;
public static bool NeedShowWhenStartup { get; set; }
public static void PostError(
Exception ex
)
{
CreateDialogueProcessIfNotExist();
_dw.WriteLine($"posterror {ex.Message}");
_dialogueProcess!.Exited -= DialogueProcessExitCallback;
}
public static void SetReadAndWriteThread(
Thread thread
)
{
_readAndWriteThreadId = thread.ManagedThreadId;
}
internal static void CreateDialogueProcessIfNotExist()
{
if (_dialogueProcess != null) {
return;
}
if (!File.Exists(_dialogueProcessInfo.FileName)) {
return;
}
_dialogueProcess = Process.Start(_dialogueProcessInfo);
_dr = new StreamReader(_dialogueClientPipe);
_dw = new StreamWriter(_dialogueServerPipe);
_dialogueProcess!.Exited += DialogueProcessExitCallback;
_dialogueServerPipe.DisposeLocalCopyOfClientHandle();
_dialogueClientPipe.DisposeLocalCopyOfClientHandle();
}
internal static void LoadDialogue()
{
_dialogueServerPipe = new AnonymousPipeServerStream(
PipeDirection.Out, HandleInheritability.Inheritable);
_dialogueClientPipe = new AnonymousPipeServerStream(
PipeDirection.In, HandleInheritability.Inheritable);
string route = Assembly.GetAssembly(typeof(World))?.Location ?? string.Empty;
string path = Path.GetDirectoryName(route) ?? string.Empty;
string exeName;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
exeName = ".exe";
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
exeName = string.Empty;
} else
{
throw new PlatformNotSupportedException("Unknown OS platform");
}
string consoleRoute = Path.Combine(
path, @"StgSharp.TerminalDialogue", $@"StgSharp.TerminalDialogue{exeName}");
_dialogueProcessInfo = new ProcessStartInfo
{
FileName = consoleRoute,
Arguments =
$"{_dialogueServerPipe.GetClientHandleAsString()} {_dialogueClientPipe.GetClientHandleAsString()}",
UseShellExecute = true,
RedirectStandardOutput = false,
};
}
internal static void PostMessage(
string instruction,
params string[] operands
)
{
CreateDialogueProcessIfNotExist();
_dw.WriteLine($"{instruction} {operands}");
}
internal static void UnloadDialogue()
{
if (_dialogueProcess != null) {
_dialogueProcess.Kill();
}
_dialogueProcess = null;
_dialogueServerPipe.Dispose();
_dialogueClientPipe.Dispose();
}
internal static string WaitCertainReturn(
string target
)
{
if (_dialogueProcess == null) {
return string.Empty;
}
string temp = string .Empty;
do
{
temp = _dr.ReadLine();
temp = temp ?? string.Empty;
if (!temp!.StartsWith(target))
{
break;
}
} while (true);
int index = temp.IndexOf(' ');
return index != -1 ? temp.Substring(index + 1) : string.Empty;
}
private static void AssertThread()
{
if (_readAndWriteThreadId != Environment.CurrentManagedThreadId) {
throw new InvalidOperationException(
"Communication with dialogue can only call from certain thread.");
}
}
private static void DialogueProcessExitCallback(
object? sender,
EventArgs e
)
{
_dialogueProcess = null;
_dr.Close();
_dw.Close();
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="Dialogue"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace StgSharp
{
public static class Dialogue
{
private static Process? _dialogueProcess;
private static AnonymousPipeServerStream _dialogueClientPipe,_dialogueServerPipe;
private static int _readAndWriteThreadId;
private static ProcessStartInfo _dialogueProcessInfo;
private static StreamReader _dr;
private static StreamWriter _dw;
public static bool NeedShowWhenStartup { get; set; }
public static void PostError(
Exception ex
)
{
CreateDialogueProcessIfNotExist();
_dw.WriteLine($"posterror {ex.Message}");
_dialogueProcess!.Exited -= DialogueProcessExitCallback;
}
public static void SetReadAndWriteThread(
Thread thread
)
{
_readAndWriteThreadId = thread.ManagedThreadId;
}
internal static void CreateDialogueProcessIfNotExist()
{
if (_dialogueProcess != null) {
return;
}
if (!File.Exists(_dialogueProcessInfo.FileName)) {
return;
}
_dialogueProcess = Process.Start(_dialogueProcessInfo);
_dr = new StreamReader(_dialogueClientPipe);
_dw = new StreamWriter(_dialogueServerPipe);
_dialogueProcess!.Exited += DialogueProcessExitCallback;
_dialogueServerPipe.DisposeLocalCopyOfClientHandle();
_dialogueClientPipe.DisposeLocalCopyOfClientHandle();
}
internal static void LoadDialogue()
{
_dialogueServerPipe = new AnonymousPipeServerStream(
PipeDirection.Out, HandleInheritability.Inheritable);
_dialogueClientPipe = new AnonymousPipeServerStream(
PipeDirection.In, HandleInheritability.Inheritable);
string route = Assembly.GetAssembly(typeof(World))?.Location ?? string.Empty;
string path = Path.GetDirectoryName(route) ?? string.Empty;
string exeName;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
exeName = ".exe";
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
exeName = string.Empty;
} else
{
throw new PlatformNotSupportedException("Unknown OS platform");
}
string consoleRoute = Path.Combine(
path, @"StgSharp.TerminalDialogue", $@"StgSharp.TerminalDialogue{exeName}");
_dialogueProcessInfo = new ProcessStartInfo
{
FileName = consoleRoute,
Arguments =
$"{_dialogueServerPipe.GetClientHandleAsString()} {_dialogueClientPipe.GetClientHandleAsString()}",
UseShellExecute = true,
RedirectStandardOutput = false,
};
}
internal static void PostMessage(
string instruction,
params string[] operands
)
{
CreateDialogueProcessIfNotExist();
_dw.WriteLine($"{instruction} {operands}");
}
internal static void UnloadDialogue()
{
if (_dialogueProcess != null) {
_dialogueProcess.Kill();
}
_dialogueProcess = null;
_dialogueServerPipe.Dispose();
_dialogueClientPipe.Dispose();
}
internal static string WaitCertainReturn(
string target
)
{
if (_dialogueProcess == null) {
return string.Empty;
}
string temp = string .Empty;
do
{
temp = _dr.ReadLine();
temp = temp ?? string.Empty;
if (!temp!.StartsWith(target))
{
break;
}
} while (true);
int index = temp.IndexOf(' ');
return index != -1 ? temp.Substring(index + 1) : string.Empty;
}
private static void AssertThread()
{
if (_readAndWriteThreadId != Environment.CurrentManagedThreadId) {
throw new InvalidOperationException(
"Communication with dialogue can only call from certain thread.");
}
}
private static void DialogueProcessExitCallback(
object? sender,
EventArgs e
)
{
_dialogueProcess = null;
_dr.Close();
_dw.Close();
}
}
}
+44 -44
View File
@@ -1,44 +1,44 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="ErrorDefine"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp
{
public class UniformEmptyReferenceException : Exception
{
public UniformEmptyReferenceException(string program, string name)
: base(
$"Location -1 returned when trying to find uniform {name} in shader program {program}") { }
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="ErrorDefine"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp
{
public class UniformEmptyReferenceException : Exception
{
public UniformEmptyReferenceException(string program, string name)
: base(
$"Location -1 returned when trying to find uniform {name} in shader program {program}") { }
}
}
+97 -97
View File
@@ -1,98 +1,98 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="GlobalSetting.cs"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp
{
public static unsafe partial class World
{
internal const int ssdSegmentLength = 16;
private static byte[] _currentHash = [];
private static bool _inited;
public static byte[] CurrentAssemblyHash
{
get
{
if (_currentHash == null)
{
string route = Assembly.GetExecutingAssembly().Location;
string time = DateTime.UtcNow.ToString();
using (SHA256 sh = SHA256.Create())
{
byte[] bytes = Encoding.UTF8.GetBytes(route);
_currentHash = sh.ComputeHash(bytes);
}
}
return _currentHash;
}
}
public static int MainThreadID
{
get => (_mainThreadID == -1) ?
throw new InvalidOperationException(
"StgSharp environment is not inited. Main thread id is not available") :
_mainThreadID;
internal set => _mainThreadID = value;
}
private static int _mainThreadID { get; set; } = -1;
public static class GlobalSetting
{
private static bool vSyncActivated;
public static bool VSyncActivated
{
get => vsyncActivated;
set
{
// GraphicFramework.glfwSwapInterval(value ? 1 : 0);
vsyncActivated = value;
}
}
}
}//-------------------------------------- End of Class ---------------------------------------//
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="GlobalSetting.cs"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp
{
public static unsafe partial class World
{
internal const int ssdSegmentLength = 16;
private static byte[] _currentHash = [];
private static bool _inited;
public static byte[] CurrentAssemblyHash
{
get
{
if (_currentHash == null)
{
string route = Assembly.GetExecutingAssembly().Location;
string time = DateTime.UtcNow.ToString();
using (SHA256 sh = SHA256.Create())
{
byte[] bytes = Encoding.UTF8.GetBytes(route);
_currentHash = sh.ComputeHash(bytes);
}
}
return _currentHash;
}
}
public static int MainThreadID
{
get => (_mainThreadID == -1) ?
throw new InvalidOperationException(
"StgSharp environment is not inited. Main thread id is not available") :
_mainThreadID;
internal set => _mainThreadID = value;
}
private static int _mainThreadID { get; set; } = -1;
public static class GlobalSetting
{
private static bool vSyncActivated;
public static bool VSyncActivated
{
get => vsyncActivated;
set
{
// GraphicFramework.glfwSwapInterval(value ? 1 : 0);
vsyncActivated = value;
}
}
}
}//-------------------------------------- End of Class ---------------------------------------//
}
+283 -283
View File
@@ -1,284 +1,284 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="IStaticModule"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
namespace StgSharp
{
/// <summary>
/// Represents a statically activated module that can be registered and initialized as part of
/// the application startup sequence, and gracefully torn down on shutdown.
/// </summary>
public interface IStaticModule
{
/// <summary>
/// Gets the display name of this module, used for logging and diagnostics.
/// </summary>
string ModuleName { get; }
/// <summary>
/// Performs all initialization work for this module using the provided profile.
/// </summary>
/// <param name="profile">
/// The configuration profile supplied at registration time. Use <see
/// cref="IModuleInitializeProfile.Empty" /> if no configuration is required.
/// </param>
void InitializeModule(IModuleInitializeProfile profile);
/// <summary>
/// Releases all resources acquired during <see cref="InitializeModule" />. Implementations should
/// be idempotent: calling this method more than once must not throw.
/// </summary>
void UninitializeModule();
}
/// <summary>
/// Marker interface for module initialization configuration. Implementations are plain data
/// carriers; no static factory is required.
/// </summary>
public interface IModuleInitializeProfile
{
/// <summary>
/// A shared, allocation-free empty profile for modules that require no configuration.
/// </summary>
static readonly IModuleInitializeProfile Empty = EmptyModuleInitializeProfile.Instance;
private sealed class EmptyModuleInitializeProfile : IModuleInitializeProfile
{
public static readonly EmptyModuleInitializeProfile Instance = new();
private EmptyModuleInitializeProfile() { }
}
}
/// <summary>
/// Pairs an <see cref="IStaticModule" /> with its <see cref="IModuleInitializeProfile" />,
/// ensuring the two are never stored or iterated independently.
/// </summary>
/// <remarks>
/// Using a <see langword="readonly struct" /> avoids the boxing overhead that would occur if
/// the pair were stored as two parallel lists of reference types.
/// </remarks>
public readonly struct ModuleDescriptor
{
/// <summary>
/// Initializes a new <see cref="ModuleDescriptor" /> using <see
/// cref="IModuleInitializeProfile.Empty" /> as the profile.
/// </summary>
public ModuleDescriptor(IStaticModule module) : this(module, IModuleInitializeProfile.Empty) { }
/// <summary>
/// Initializes a new <see cref="ModuleDescriptor" /> with an explicit profile.
/// </summary>
public ModuleDescriptor(IStaticModule module, IModuleInitializeProfile profile)
{
Module = module;
Profile = profile;
}
/// <summary>
/// Gets the configuration profile associated with the module.
/// </summary>
public IModuleInitializeProfile Profile { get; }
/// <summary>
/// Gets the module to be initialized.
/// </summary>
public IStaticModule Module { get; }
/// <summary>
/// Calls <see cref="IStaticModule.InitializeModule" /> with the stored profile.
/// </summary>
public void Initialize()
{
Module.InitializeModule(Profile);
}
/// <summary>
/// Calls <see cref="IStaticModule.UninitializeModule" /> on the stored module.
/// </summary>
public void Uninitialize()
{
Module.UninitializeModule();
}
}
/// <summary>
/// An ordered collection of modules to be initialized during application startup and
/// uninitialized on shutdown. Supports three registration strategies so that the <c>new()</c>
/// generic constraint is never a hard requirement.
/// </summary>
public sealed class ModuleToInitializeCollection : IEnumerable<IStaticModule>, IStaticModule
{
private readonly List<ModuleDescriptor> _descriptors = [];
internal ModuleToInitializeCollection(World.Initializer i)
{
_descriptors.Add(new ModuleDescriptor(i));
}
// ── IEnumerable<IStaticModule> ───────────────────────────────────────────
/// <inheritdoc />
public IEnumerator<IStaticModule> GetEnumerator()
{
foreach (ModuleDescriptor d in _descriptors) {
yield return d.Module;
}
}
// ── Initialization / Uninitialization ────────────────────────────────────
/// <summary>
/// Initializes all registered modules in the order they were registered. Each module
/// receives the profile that was supplied at registration time.
/// </summary>
public void InitializeModule()
{
for (int i = 0; i < _descriptors.Count; i++) {
_descriptors[i].Initialize();
}
}
/// <summary>
/// Uninitializes all registered modules in <b> reverse </b> registration order, mirroring
/// the typical LIFO teardown convention (e.g., GLFW must be unloaded after all subsystems
/// that depend on it).
/// </summary>
public void UninitializeModule()
{
for (int i = _descriptors.Count - 1; i >= 0; i--) {
_descriptors[i].Uninitialize();
}
}
// ── Registration API ─────────────────────────────────────────────────────
/// <summary>
/// Registers a module of type <typeparamref name="T" /> using its default constructor.
/// This is the most concise overload for simple modules that expose a public
/// parameterless constructor.
/// </summary>
/// <typeparam name="T">
/// A concrete module type that implements <see cref="IStaticModule" /> and exposes a
/// public parameterless constructor.
/// </typeparam>
/// <param name="profile">
/// Optional initialization profile. Defaults to <see
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
/// </param>
public ModuleToInitializeCollection UseModule<T>(IModuleInitializeProfile? profile = null)
where T: IStaticModule, new()
{
return UseModule(static() => new T(), profile);
}
/// <summary>
/// Registers a module produced by a factory delegate. This overload imposes no generic
/// constraints and supports any construction strategy, including dependency injection and
/// parameterized constructors.
/// </summary>
/// <param name="factory">
/// A delegate that constructs and returns the module instance. The delegate is invoked
/// once, immediately at registration time. If <paramref name="factory" /> is <see
/// langword="null" /> or returns <see langword="null" />, the call is silently ignored
/// and the collection is returned unchanged.
/// </param>
/// <param name="profile">
/// Optional initialization profile. Defaults to <see
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
/// </param>
public ModuleToInitializeCollection UseModule(
Func<IStaticModule> factory,
IModuleInitializeProfile? profile = null)
{
if (factory is null) {
return this;
}
IStaticModule? mod = factory();
if (mod is null) {
return this;
}
_descriptors.Add(new ModuleDescriptor(
mod,
profile ?? IModuleInitializeProfile.Empty));
return this;
}
/// <summary>
/// Registers a pre-existing module instance. Use this overload when the module is
/// provided externally, such as from a DI container, an object pool, or a singleton.
/// </summary>
/// <param name="module">
/// The module instance to register.
/// </param>
/// <param name="profile">
/// Optional initialization profile. Defaults to <see
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
/// </param>
public ModuleToInitializeCollection UseModule(IStaticModule module, IModuleInitializeProfile? profile = null)
{
_descriptors.Add(new ModuleDescriptor(
module,
profile ?? IModuleInitializeProfile.Empty));
return this;
}
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
// ── IStaticModule explicit implementation ────────────────────────────────
/// <summary>
/// Explicit <see cref="IStaticModule" /> implementation. Delegates to <see
/// cref="InitializeModule()" />, ignoring the supplied profile because each contained module uses
/// its own registered profile.
/// </summary>
void IStaticModule.InitializeModule(IModuleInitializeProfile profile)
{
InitializeModule();
}
/// <inheritdoc />
string IStaticModule.ModuleName => "Module Initializer";
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="IStaticModule"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
namespace StgSharp
{
/// <summary>
/// Represents a statically activated module that can be registered and initialized as part of
/// the application startup sequence, and gracefully torn down on shutdown.
/// </summary>
public interface IStaticModule
{
/// <summary>
/// Gets the display name of this module, used for logging and diagnostics.
/// </summary>
string ModuleName { get; }
/// <summary>
/// Performs all initialization work for this module using the provided profile.
/// </summary>
/// <param name="profile">
/// The configuration profile supplied at registration time. Use <see
/// cref="IModuleInitializeProfile.Empty" /> if no configuration is required.
/// </param>
void InitializeModule(IModuleInitializeProfile profile);
/// <summary>
/// Releases all resources acquired during <see cref="InitializeModule" />. Implementations should
/// be idempotent: calling this method more than once must not throw.
/// </summary>
void UninitializeModule();
}
/// <summary>
/// Marker interface for module initialization configuration. Implementations are plain data
/// carriers; no static factory is required.
/// </summary>
public interface IModuleInitializeProfile
{
/// <summary>
/// A shared, allocation-free empty profile for modules that require no configuration.
/// </summary>
static readonly IModuleInitializeProfile Empty = EmptyModuleInitializeProfile.Instance;
private sealed class EmptyModuleInitializeProfile : IModuleInitializeProfile
{
public static readonly EmptyModuleInitializeProfile Instance = new();
private EmptyModuleInitializeProfile() { }
}
}
/// <summary>
/// Pairs an <see cref="IStaticModule" /> with its <see cref="IModuleInitializeProfile" />,
/// ensuring the two are never stored or iterated independently.
/// </summary>
/// <remarks>
/// Using a <see langword="readonly struct" /> avoids the boxing overhead that would occur if
/// the pair were stored as two parallel lists of reference types.
/// </remarks>
public readonly struct ModuleDescriptor
{
/// <summary>
/// Initializes a new <see cref="ModuleDescriptor" /> using <see
/// cref="IModuleInitializeProfile.Empty" /> as the profile.
/// </summary>
public ModuleDescriptor(IStaticModule module) : this(module, IModuleInitializeProfile.Empty) { }
/// <summary>
/// Initializes a new <see cref="ModuleDescriptor" /> with an explicit profile.
/// </summary>
public ModuleDescriptor(IStaticModule module, IModuleInitializeProfile profile)
{
Module = module;
Profile = profile;
}
/// <summary>
/// Gets the configuration profile associated with the module.
/// </summary>
public IModuleInitializeProfile Profile { get; }
/// <summary>
/// Gets the module to be initialized.
/// </summary>
public IStaticModule Module { get; }
/// <summary>
/// Calls <see cref="IStaticModule.InitializeModule" /> with the stored profile.
/// </summary>
public void Initialize()
{
Module.InitializeModule(Profile);
}
/// <summary>
/// Calls <see cref="IStaticModule.UninitializeModule" /> on the stored module.
/// </summary>
public void Uninitialize()
{
Module.UninitializeModule();
}
}
/// <summary>
/// An ordered collection of modules to be initialized during application startup and
/// uninitialized on shutdown. Supports three registration strategies so that the <c>new()</c>
/// generic constraint is never a hard requirement.
/// </summary>
public sealed class ModuleToInitializeCollection : IEnumerable<IStaticModule>, IStaticModule
{
private readonly List<ModuleDescriptor> _descriptors = [];
internal ModuleToInitializeCollection(World.Initializer i)
{
_descriptors.Add(new ModuleDescriptor(i));
}
// ── IEnumerable<IStaticModule> ───────────────────────────────────────────
/// <inheritdoc />
public IEnumerator<IStaticModule> GetEnumerator()
{
foreach (ModuleDescriptor d in _descriptors) {
yield return d.Module;
}
}
// ── Initialization / Uninitialization ────────────────────────────────────
/// <summary>
/// Initializes all registered modules in the order they were registered. Each module
/// receives the profile that was supplied at registration time.
/// </summary>
public void InitializeModule()
{
for (int i = 0; i < _descriptors.Count; i++) {
_descriptors[i].Initialize();
}
}
/// <summary>
/// Uninitializes all registered modules in <b> reverse </b> registration order, mirroring
/// the typical LIFO teardown convention (e.g., GLFW must be unloaded after all subsystems
/// that depend on it).
/// </summary>
public void UninitializeModule()
{
for (int i = _descriptors.Count - 1; i >= 0; i--) {
_descriptors[i].Uninitialize();
}
}
// ── Registration API ─────────────────────────────────────────────────────
/// <summary>
/// Registers a module of type <typeparamref name="T" /> using its default constructor.
/// This is the most concise overload for simple modules that expose a public
/// parameterless constructor.
/// </summary>
/// <typeparam name="T">
/// A concrete module type that implements <see cref="IStaticModule" /> and exposes a
/// public parameterless constructor.
/// </typeparam>
/// <param name="profile">
/// Optional initialization profile. Defaults to <see
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
/// </param>
public ModuleToInitializeCollection UseModule<T>(IModuleInitializeProfile? profile = null)
where T: IStaticModule, new()
{
return UseModule(static() => new T(), profile);
}
/// <summary>
/// Registers a module produced by a factory delegate. This overload imposes no generic
/// constraints and supports any construction strategy, including dependency injection and
/// parameterized constructors.
/// </summary>
/// <param name="factory">
/// A delegate that constructs and returns the module instance. The delegate is invoked
/// once, immediately at registration time. If <paramref name="factory" /> is <see
/// langword="null" /> or returns <see langword="null" />, the call is silently ignored
/// and the collection is returned unchanged.
/// </param>
/// <param name="profile">
/// Optional initialization profile. Defaults to <see
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
/// </param>
public ModuleToInitializeCollection UseModule(
Func<IStaticModule> factory,
IModuleInitializeProfile? profile = null)
{
if (factory is null) {
return this;
}
IStaticModule? mod = factory();
if (mod is null) {
return this;
}
_descriptors.Add(new ModuleDescriptor(
mod,
profile ?? IModuleInitializeProfile.Empty));
return this;
}
/// <summary>
/// Registers a pre-existing module instance. Use this overload when the module is
/// provided externally, such as from a DI container, an object pool, or a singleton.
/// </summary>
/// <param name="module">
/// The module instance to register.
/// </param>
/// <param name="profile">
/// Optional initialization profile. Defaults to <see
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
/// </param>
public ModuleToInitializeCollection UseModule(IStaticModule module, IModuleInitializeProfile? profile = null)
{
_descriptors.Add(new ModuleDescriptor(
module,
profile ?? IModuleInitializeProfile.Empty));
return this;
}
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
// ── IStaticModule explicit implementation ────────────────────────────────
/// <summary>
/// Explicit <see cref="IStaticModule" /> implementation. Delegates to <see
/// cref="InitializeModule()" />, ignoring the supplied profile because each contained module uses
/// its own registered profile.
/// </summary>
void IStaticModule.InitializeModule(IModuleInitializeProfile profile)
{
InitializeModule();
}
/// <inheritdoc />
string IStaticModule.ModuleName => "Module Initializer";
}
}
+236 -236
View File
@@ -1,237 +1,237 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="Platform"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Mathematics;
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace StgSharp
{
internal enum GraphicAPI : byte
{
GL,
VK
}
public static partial class World
{
/// <summary>
/// Version of current World platform. Main version represent in hundred's digit, and sub
/// version represent in ten's digit.
/// </summary>
public const long version = 430;
private static bool vsyncActivated;
private static uint markCount = 0;
internal static GraphicAPI API = default;
public static ModuleToInitializeCollection Initialize()
{
return new ModuleToInitializeCollection(new Initializer());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void LogError(
Exception ex
)
{
DefaultLog.InternalWriteLog(ex.Message, LogType.Error);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void LogInfo(
string log
)
{
DefaultLog.InternalWriteLog(log, LogType.Info);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void LogWarning(
string log
)
{
DefaultLog.InternalWriteLog(log, LogType.Warning);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void LogWarning(
Exception notVerySeriesException
)
{
DefaultLog.InternalWriteLog(notVerySeriesException.Message, LogType.Warning);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Mark(
bool needConsole
)
{
markCount++;
if (needConsole) {
Console.WriteLine(markCount);
}
}
public static void Terminate(
int exitCode
)
{
MainTimeProvider.StopProvidingTime();
// GraphicFramework.glfwTerminate();
Environment.Exit(exitCode);
}
public static void Track(
object target,
string message,
bool isConsole,
[CallerFilePath] string filePath = "",
[CallerMemberName] string callerName = "",
[CallerLineNumber] int lineNumber = 0
)
{
Type t = target.GetType();
string log = $"Tracked object: {nameof(target)}\t\tType:{t.Name}\n";
FieldInfo[] fieldInfo = t.GetFields(
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
log += "|\tFieldName\t|\tFieldType\t|\tFieldValue\t|\n";
foreach (FieldInfo field in fieldInfo) {
log += $"|{field.Name}\t\t|{field.FieldType}\t\t|{field.GetValue(target)}\t\t|\n";
}
log += $"Tracking at:\n{filePath}\t{callerName}\tline {lineNumber}\n";
log += $"Additional information:\n{message}\n";
if (isConsole)
{
Console.WriteLine(log);
} else
{
DefaultLog.InternalWriteLog(log, LogType.Info);
}
}
/// <summary>
/// Write a log message to log file, and mark the _level of this message.
/// </summary>
/// <param _label="message">
/// A string contains information of how current evironment runs.
/// </param>
/// <param _label="logType">
/// The severity of current message.
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteLog(
string message,
LogType logType
)
{
DefaultLog.InternalWriteLog(message, logType);
}
internal static TDele ConvertAPI<TDele>(
IntPtr funcPtr
)
{
if (funcPtr == IntPtr.Zero) {
throw new InvalidCastException(
"Failed to convert api, the function pointer is zero.");
}
try
{
return Marshal.GetDelegateForFunctionPointer<TDele>(funcPtr);
}
catch (Exception)
{
throw;
}
}
internal static Delegate ConvertAPI(
IntPtr funcPtr,
Type T
)
{
if (funcPtr == IntPtr.Zero) {
throw new InvalidCastException(
"Failed to convert api, the function pointer is zero.");
}
try
{
return Marshal.GetDelegateForFunctionPointer(funcPtr, T);
}
catch (Exception)
{
throw;
}
}
internal static unsafe void InternalInitialize()
{
// Initialize time provider and start providing time
MainTimeProvider.StartProvidingTime();
DefaultLog.InternalAppendLog("\n\n\n");
DefaultLog.InternalWriteLog(
$"Program {Assembly.GetEntryAssembly()!.FullName} on {Environment.MachineName} Started.", LogType.Info);
MainThreadID = System.Environment.CurrentManagedThreadId;
/**/
Dialogue.NeedShowWhenStartup = false;
Dialogue.LoadDialogue();
if (Dialogue.NeedShowWhenStartup) {
Dialogue.CreateDialogueProcessIfNotExist();
}
/**/
}
internal class Initializer : IStaticModule
{
public string ModuleName => "StgSharpCore";
public void InitializeModule(
IModuleInitializeProfile profile
)
{
World.InternalInitialize();
}
public void UninitializeModule()
{
throw new NotImplementedException();
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="Platform"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Mathematics;
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace StgSharp
{
internal enum GraphicAPI : byte
{
GL,
VK
}
public static partial class World
{
/// <summary>
/// Version of current World platform. Main version represent in hundred's digit, and sub
/// version represent in ten's digit.
/// </summary>
public const long version = 430;
private static bool vsyncActivated;
private static uint markCount = 0;
internal static GraphicAPI API = default;
public static ModuleToInitializeCollection Initialize()
{
return new ModuleToInitializeCollection(new Initializer());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void LogError(
Exception ex
)
{
DefaultLog.InternalWriteLog(ex.Message, LogType.Error);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void LogInfo(
string log
)
{
DefaultLog.InternalWriteLog(log, LogType.Info);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void LogWarning(
string log
)
{
DefaultLog.InternalWriteLog(log, LogType.Warning);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void LogWarning(
Exception notVerySeriesException
)
{
DefaultLog.InternalWriteLog(notVerySeriesException.Message, LogType.Warning);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Mark(
bool needConsole
)
{
markCount++;
if (needConsole) {
Console.WriteLine(markCount);
}
}
public static void Terminate(
int exitCode
)
{
MainTimeProvider.StopProvidingTime();
// GraphicFramework.glfwTerminate();
Environment.Exit(exitCode);
}
public static void Track(
object target,
string message,
bool isConsole,
[CallerFilePath] string filePath = "",
[CallerMemberName] string callerName = "",
[CallerLineNumber] int lineNumber = 0
)
{
Type t = target.GetType();
string log = $"Tracked object: {nameof(target)}\t\tType:{t.Name}\n";
FieldInfo[] fieldInfo = t.GetFields(
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
log += "|\tFieldName\t|\tFieldType\t|\tFieldValue\t|\n";
foreach (FieldInfo field in fieldInfo) {
log += $"|{field.Name}\t\t|{field.FieldType}\t\t|{field.GetValue(target)}\t\t|\n";
}
log += $"Tracking at:\n{filePath}\t{callerName}\tline {lineNumber}\n";
log += $"Additional information:\n{message}\n";
if (isConsole)
{
Console.WriteLine(log);
} else
{
DefaultLog.InternalWriteLog(log, LogType.Info);
}
}
/// <summary>
/// Write a log message to log file, and mark the _level of this message.
/// </summary>
/// <param _label="message">
/// A string contains information of how current evironment runs.
/// </param>
/// <param _label="logType">
/// The severity of current message.
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteLog(
string message,
LogType logType
)
{
DefaultLog.InternalWriteLog(message, logType);
}
internal static TDele ConvertAPI<TDele>(
IntPtr funcPtr
)
{
if (funcPtr == IntPtr.Zero) {
throw new InvalidCastException(
"Failed to convert api, the function pointer is zero.");
}
try
{
return Marshal.GetDelegateForFunctionPointer<TDele>(funcPtr);
}
catch (Exception)
{
throw;
}
}
internal static Delegate ConvertAPI(
IntPtr funcPtr,
Type T
)
{
if (funcPtr == IntPtr.Zero) {
throw new InvalidCastException(
"Failed to convert api, the function pointer is zero.");
}
try
{
return Marshal.GetDelegateForFunctionPointer(funcPtr, T);
}
catch (Exception)
{
throw;
}
}
internal static unsafe void InternalInitialize()
{
// Initialize time provider and start providing time
MainTimeProvider.StartProvidingTime();
DefaultLog.InternalAppendLog("\n\n\n");
DefaultLog.InternalWriteLog(
$"Program {Assembly.GetEntryAssembly()!.FullName} on {Environment.MachineName} Started.", LogType.Info);
MainThreadID = System.Environment.CurrentManagedThreadId;
/**/
Dialogue.NeedShowWhenStartup = false;
Dialogue.LoadDialogue();
if (Dialogue.NeedShowWhenStartup) {
Dialogue.CreateDialogueProcessIfNotExist();
}
/**/
}
internal class Initializer : IStaticModule
{
public string ModuleName => "StgSharpCore";
public void InitializeModule(
IModuleInitializeProfile profile
)
{
World.InternalInitialize();
}
public void UninitializeModule()
{
throw new NotImplementedException();
}
}
}
}
+37 -37
View File
@@ -1,37 +1,37 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="Global"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
global using StgSharp.Internal;
global
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("StgSharp.Mathematics")]
[assembly: InternalsVisibleTo("StgSharp.RegularAnalysis")]
[assembly: InternalsVisibleTo("StgSharp.Graphics")]
[assembly: InternalsVisibleTo("StgSharpDebug")]
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="Global"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
global using StgSharp.Internal;
global
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("StgSharp.Mathematics")]
[assembly: InternalsVisibleTo("StgSharp.RegularAnalysis")]
[assembly: InternalsVisibleTo("StgSharp.Graphics")]
[assembly: InternalsVisibleTo("StgSharpDebug")]
@@ -1,163 +1,163 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="BufferStack"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace StgSharp.Mathematics.Memory
{
[CollectionBuilder(typeof(BufferStackBuilder), nameof(BufferStackBuilder.Create))]
public unsafe class BufferStack<T> : IDisposable where T: unmanaged
{
private byte* _buffer;
private ulong _count;
public BufferStack(int capacity)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(capacity);
AllocateBuffer(capacity);
}
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (int)_count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
((Header*)_buffer)->Top = 0;
_count = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
Marshal.FreeHGlobal((nint)_buffer);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Pop()
{
Header* header = (Header*)_buffer;
if (header->Top == 0) {
throw new InvalidOperationException("Stack underflow");
}
header->Top--;
_count--;
T* dataStart = (T*)(_buffer + sizeof(Header));
return dataStart[header->Top];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Push(T item)
{
Header* header = (Header*)_buffer;
if (header->Top >= header->Capacity)
{
Expand();
header = (Header*)_buffer;
}
T* dataStart = (T*)(_buffer + sizeof(Header));
dataStart[header->Top] = item;
header->Top++;
_count++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T value)
{
Header* header = (Header*)_buffer;
if (header->Top == 0)
{
value = default;
return false;
}
header->Top--;
_count--;
T* dataStart = (T*)(_buffer + sizeof(Header));
value = dataStart[header->Top];
return true;
}
// Internal method for efficient bulk initialization
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal unsafe void InitializeFromSpan(ReadOnlySpan<T> span)
{
if (span.IsEmpty) {
return;
}
Header* header = (Header*)_buffer;
T* dataStart = (T*)(_buffer + sizeof(Header));
fixed (T* sourcePtr = span) {
Buffer.MemoryCopy(sourcePtr, dataStart, header->Capacity * (nuint)sizeof(T),
(nuint)span.Length * (nuint)sizeof(T));
}
header->Top = (nuint)span.Length;
_count = (ulong)span.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AllocateBuffer(int capacity)
{
nuint size = (nuint)sizeof(Header) + ((nuint)capacity * (nuint)sizeof(T));
_buffer = (byte*)NativeMemory.Alloc(size);
Header* header = (Header*)_buffer;
header->Top = 0;
header->Capacity = (nuint)capacity;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Expand()
{
Header* header = (Header*)_buffer;
nuint newCap = header->Capacity * 2;
nuint newSize = (nuint)sizeof(Header) + (newCap * (nuint)sizeof(T));
_buffer = (byte*)NativeMemory.Realloc(_buffer, newSize);
header = (Header*)_buffer;
header->Capacity = newCap;
}
private struct Header
{
public nuint Capacity;
public nuint Top;
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="BufferStack"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace StgSharp.Mathematics.Memory
{
[CollectionBuilder(typeof(BufferStackBuilder), nameof(BufferStackBuilder.Create))]
public unsafe class BufferStack<T> : IDisposable where T: unmanaged
{
private byte* _buffer;
private ulong _count;
public BufferStack(int capacity)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(capacity);
AllocateBuffer(capacity);
}
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (int)_count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
((Header*)_buffer)->Top = 0;
_count = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
Marshal.FreeHGlobal((nint)_buffer);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Pop()
{
Header* header = (Header*)_buffer;
if (header->Top == 0) {
throw new InvalidOperationException("Stack underflow");
}
header->Top--;
_count--;
T* dataStart = (T*)(_buffer + sizeof(Header));
return dataStart[header->Top];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Push(T item)
{
Header* header = (Header*)_buffer;
if (header->Top >= header->Capacity)
{
Expand();
header = (Header*)_buffer;
}
T* dataStart = (T*)(_buffer + sizeof(Header));
dataStart[header->Top] = item;
header->Top++;
_count++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T value)
{
Header* header = (Header*)_buffer;
if (header->Top == 0)
{
value = default;
return false;
}
header->Top--;
_count--;
T* dataStart = (T*)(_buffer + sizeof(Header));
value = dataStart[header->Top];
return true;
}
// Internal method for efficient bulk initialization
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal unsafe void InitializeFromSpan(ReadOnlySpan<T> span)
{
if (span.IsEmpty) {
return;
}
Header* header = (Header*)_buffer;
T* dataStart = (T*)(_buffer + sizeof(Header));
fixed (T* sourcePtr = span) {
Buffer.MemoryCopy(sourcePtr, dataStart, header->Capacity * (nuint)sizeof(T),
(nuint)span.Length * (nuint)sizeof(T));
}
header->Top = (nuint)span.Length;
_count = (ulong)span.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AllocateBuffer(int capacity)
{
nuint size = (nuint)sizeof(Header) + ((nuint)capacity * (nuint)sizeof(T));
_buffer = (byte*)NativeMemory.Alloc(size);
Header* header = (Header*)_buffer;
header->Top = 0;
header->Capacity = (nuint)capacity;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Expand()
{
Header* header = (Header*)_buffer;
nuint newCap = header->Capacity * 2;
nuint newSize = (nuint)sizeof(Header) + (newCap * (nuint)sizeof(T));
_buffer = (byte*)NativeMemory.Realloc(_buffer, newSize);
header = (Header*)_buffer;
header->Capacity = newCap;
}
private struct Header
{
public nuint Capacity;
public nuint Top;
}
}
}
@@ -1,308 +1,308 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="ConcurrentBufferStack"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Threading;
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
namespace StgSharp.Mathematics.Memory
{
public static class BufferStackBuilder
{
// For BufferStack collection builder
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BufferStack<T> Create<T>(ReadOnlySpan<T> span) where T: unmanaged
{
if (span.IsEmpty)
{
return new BufferStack<T>(4); // Default small capacity
}
BufferStack<T> stack = new(span.Length);
// Use internal method for efficient bulk initialization
stack.InitializeFromSpan(span);
return stack;
}
// For ConcurrentBufferStack collection builder
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ConcurrentBufferStack<T> CreateConcurrent<T>(ReadOnlySpan<T> span) where T: unmanaged
{
if (span.IsEmpty)
{
return new ConcurrentBufferStack<T>(4); // Default small capacity
}
ConcurrentBufferStack<T> stack = new(span.Length);
// Use internal method for efficient bulk initialization
stack.InitializeFromSpan(span);
return stack;
}
}
[CollectionBuilder(builderType: typeof(BufferStackBuilder), methodName: nameof(BufferStackBuilder.CreateConcurrent))]
public unsafe class ConcurrentBufferStack<T> : IDisposable where T: unmanaged
{
private byte* _buffer;
private readonly BufferExpansionLock _lock = new();
private long _count;
public ConcurrentBufferStack()
{
AllocateBuffer(4);
}
public ConcurrentBufferStack(int capacity)
{
if (capacity <= 0) {
throw new ArgumentOutOfRangeException(nameof(capacity));
}
AllocateBuffer(capacity);
}
public ulong Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (ulong)Volatile.Read(ref _count);
}
public void Clear()
{
_lock.EnterExpansionProcess();
try
{
_lock.EnterBufferCopy(); // exclusive while mutating Top
try
{
Header* header = (Header*)_buffer;
header->Top = 0;
Volatile.Write(ref _count, 0);
}
finally
{
_lock.ExitBufferCopy();
}
}
finally
{
_lock.ExitExpansionProcess();
}
}
public void Dispose()
{
_lock.Dispose();
NativeMemory.Free(_buffer);
}
public T Pop()
{
while (true)
{
_lock.EnterBufferRead(); // prevent buffer being reallocated while reading/modifying Top
try
{
Header* header = (Header*)_buffer;
int oldTop = Volatile.Read(ref header->Top);
if (oldTop <= 0) {
throw new InvalidOperationException("Stack underflow");
}
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
{
T* dataStart = (T*)(_buffer + sizeof(Header));
_ = Interlocked.Decrement(ref _count);
return dataStart[oldTop - 1];
}
}
finally
{
_lock.ExitBufferRead();
}
Thread.Sleep(0);
}
}
public void Push(T item)
{
while (true)
{
// Ensure capacity, expanding if needed
_lock.EnterMetaDataRead();
try
{
Header* header = (Header*)_buffer;
int topSnapshot = Volatile.Read(ref header->Top);
int cap = Volatile.Read(ref header->Capacity);
if (topSnapshot >= cap)
{
// Need expansion
_lock.ExitMetaDataRead();
_lock.EnterExpansionProcess();
try
{
header = (Header*)_buffer;
if (header->Top >= header->Capacity)
{
int newCap = header->Capacity * 2;
nuint newSize = (nuint)sizeof(Header) + ((nuint)newCap * (nuint)sizeof(T));
_lock.EnterBufferCopy();
try
{
_buffer = (byte*)NativeMemory.Realloc(_buffer, newSize);
((Header*)_buffer)->Capacity = newCap;
}
finally
{
_lock.ExitBufferCopy();
}
}
}
finally
{
_lock.ExitExpansionProcess();
}
continue;
}
}
finally
{
if (_lock.IsThreadReadingMetaData) {
_lock.ExitMetaDataRead();
}
}
// Reserve and write under exclusive buffer lock to avoid consumers reading unwritten data
_lock.EnterBufferCopy();
try
{
Header* header = (Header*)_buffer;
int idx = header->Top; // reserve next slot
if (idx >= header->Capacity)
{
// lost race against expander or other producer; retry
continue;
}
T* dataStart = (T*)(_buffer + sizeof(Header));
dataStart[idx] = item; // write first
header->Top = idx + 1; // then publish by increasing top
_ = Interlocked.Increment(ref _count);
return;
}
finally
{
_lock.ExitBufferCopy();
}
}
}
public bool TryPop(out T value)
{
while (true)
{
_lock.EnterBufferRead();
try
{
Header* header = (Header*)_buffer;
int oldTop = Volatile.Read(ref header->Top);
if (oldTop <= 0)
{
value = default;
return false;
}
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
{
T* dataStart = (T*)(_buffer + sizeof(Header));
value = dataStart[oldTop - 1];
_ = Interlocked.Decrement(ref _count);
return true;
}
}
finally
{
_lock.ExitBufferRead();
}
Thread.Sleep(0);
}
}
// Internal method for efficient bulk initialization
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal unsafe void InitializeFromSpan(ReadOnlySpan<T> span)
{
if (span.IsEmpty) {
return;
}
Header* header = (Header*)_buffer;
T* dataStart = (T*)(_buffer + sizeof(Header));
fixed (T* sourcePtr = span) {
Buffer.MemoryCopy(sourcePtr, dataStart, (nuint)header->Capacity * (nuint)sizeof(T),
(nuint)span.Length * (nuint)sizeof(T));
}
header->Top = span.Length;
Volatile.Write(ref _count, span.Length);
}
private void AllocateBuffer(int capacity)
{
nuint size = (nuint)sizeof(Header) + (nuint)capacity * (nuint)sizeof(T);
_buffer = (byte*)NativeMemory.Alloc(size);
Header* header = (Header*)_buffer;
header->Top = 0;
header->Capacity = capacity;
}
private struct Header
{
public int Capacity; // max capacity of the stack (number of elements)
public int Top; // next available index in the stack
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="ConcurrentBufferStack"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Threading;
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
namespace StgSharp.Mathematics.Memory
{
public static class BufferStackBuilder
{
// For BufferStack collection builder
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BufferStack<T> Create<T>(ReadOnlySpan<T> span) where T: unmanaged
{
if (span.IsEmpty)
{
return new BufferStack<T>(4); // Default small capacity
}
BufferStack<T> stack = new(span.Length);
// Use internal method for efficient bulk initialization
stack.InitializeFromSpan(span);
return stack;
}
// For ConcurrentBufferStack collection builder
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ConcurrentBufferStack<T> CreateConcurrent<T>(ReadOnlySpan<T> span) where T: unmanaged
{
if (span.IsEmpty)
{
return new ConcurrentBufferStack<T>(4); // Default small capacity
}
ConcurrentBufferStack<T> stack = new(span.Length);
// Use internal method for efficient bulk initialization
stack.InitializeFromSpan(span);
return stack;
}
}
[CollectionBuilder(builderType: typeof(BufferStackBuilder), methodName: nameof(BufferStackBuilder.CreateConcurrent))]
public unsafe class ConcurrentBufferStack<T> : IDisposable where T: unmanaged
{
private byte* _buffer;
private readonly BufferExpansionLock _lock = new();
private long _count;
public ConcurrentBufferStack()
{
AllocateBuffer(4);
}
public ConcurrentBufferStack(int capacity)
{
if (capacity <= 0) {
throw new ArgumentOutOfRangeException(nameof(capacity));
}
AllocateBuffer(capacity);
}
public ulong Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (ulong)Volatile.Read(ref _count);
}
public void Clear()
{
_lock.EnterExpansionProcess();
try
{
_lock.EnterBufferCopy(); // exclusive while mutating Top
try
{
Header* header = (Header*)_buffer;
header->Top = 0;
Volatile.Write(ref _count, 0);
}
finally
{
_lock.ExitBufferCopy();
}
}
finally
{
_lock.ExitExpansionProcess();
}
}
public void Dispose()
{
_lock.Dispose();
NativeMemory.Free(_buffer);
}
public T Pop()
{
while (true)
{
_lock.EnterBufferRead(); // prevent buffer being reallocated while reading/modifying Top
try
{
Header* header = (Header*)_buffer;
int oldTop = Volatile.Read(ref header->Top);
if (oldTop <= 0) {
throw new InvalidOperationException("Stack underflow");
}
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
{
T* dataStart = (T*)(_buffer + sizeof(Header));
_ = Interlocked.Decrement(ref _count);
return dataStart[oldTop - 1];
}
}
finally
{
_lock.ExitBufferRead();
}
Thread.Sleep(0);
}
}
public void Push(T item)
{
while (true)
{
// Ensure capacity, expanding if needed
_lock.EnterMetaDataRead();
try
{
Header* header = (Header*)_buffer;
int topSnapshot = Volatile.Read(ref header->Top);
int cap = Volatile.Read(ref header->Capacity);
if (topSnapshot >= cap)
{
// Need expansion
_lock.ExitMetaDataRead();
_lock.EnterExpansionProcess();
try
{
header = (Header*)_buffer;
if (header->Top >= header->Capacity)
{
int newCap = header->Capacity * 2;
nuint newSize = (nuint)sizeof(Header) + ((nuint)newCap * (nuint)sizeof(T));
_lock.EnterBufferCopy();
try
{
_buffer = (byte*)NativeMemory.Realloc(_buffer, newSize);
((Header*)_buffer)->Capacity = newCap;
}
finally
{
_lock.ExitBufferCopy();
}
}
}
finally
{
_lock.ExitExpansionProcess();
}
continue;
}
}
finally
{
if (_lock.IsThreadReadingMetaData) {
_lock.ExitMetaDataRead();
}
}
// Reserve and write under exclusive buffer lock to avoid consumers reading unwritten data
_lock.EnterBufferCopy();
try
{
Header* header = (Header*)_buffer;
int idx = header->Top; // reserve next slot
if (idx >= header->Capacity)
{
// lost race against expander or other producer; retry
continue;
}
T* dataStart = (T*)(_buffer + sizeof(Header));
dataStart[idx] = item; // write first
header->Top = idx + 1; // then publish by increasing top
_ = Interlocked.Increment(ref _count);
return;
}
finally
{
_lock.ExitBufferCopy();
}
}
}
public bool TryPop(out T value)
{
while (true)
{
_lock.EnterBufferRead();
try
{
Header* header = (Header*)_buffer;
int oldTop = Volatile.Read(ref header->Top);
if (oldTop <= 0)
{
value = default;
return false;
}
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
{
T* dataStart = (T*)(_buffer + sizeof(Header));
value = dataStart[oldTop - 1];
_ = Interlocked.Decrement(ref _count);
return true;
}
}
finally
{
_lock.ExitBufferRead();
}
Thread.Sleep(0);
}
}
// Internal method for efficient bulk initialization
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal unsafe void InitializeFromSpan(ReadOnlySpan<T> span)
{
if (span.IsEmpty) {
return;
}
Header* header = (Header*)_buffer;
T* dataStart = (T*)(_buffer + sizeof(Header));
fixed (T* sourcePtr = span) {
Buffer.MemoryCopy(sourcePtr, dataStart, (nuint)header->Capacity * (nuint)sizeof(T),
(nuint)span.Length * (nuint)sizeof(T));
}
header->Top = span.Length;
Volatile.Write(ref _count, span.Length);
}
private void AllocateBuffer(int capacity)
{
nuint size = (nuint)sizeof(Header) + (nuint)capacity * (nuint)sizeof(T);
_buffer = (byte*)NativeMemory.Alloc(size);
Header* header = (Header*)_buffer;
header->Top = 0;
header->Capacity = capacity;
}
private struct Header
{
public int Capacity; // max capacity of the stack (number of elements)
public int Top; // next available index in the stack
}
}
}
@@ -1,114 +1,114 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HLSFAllocationHandle"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Allocator = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocator;
namespace StgSharp.Mathematics.Memory
{
public readonly unsafe struct HybridLayerSegregatedFitAllocationHandle
{
internal readonly Allocator.Entry* EntryHandle;
public readonly long AllocSize;
internal HybridLayerSegregatedFitAllocationHandle(Allocator.Entry* handle, long s)
{
EntryHandle = handle;
Pointer = (byte*)handle->Position;
AllocSize = s;
// Console.WriteLine($"{(ulong)handle->PreviousNear}<->{(ulong)handle->NextNear},\t{handle->Size}");
}
public readonly ref byte this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, AllocSize);
return ref Pointer[index];
}
}
public readonly byte* Pointer { get; init; }
public readonly Span<byte> BufferHandle
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(Pointer, (int)AllocSize);
}
public readonly Enumerator GetEnumerator()
{
return new Enumerator(this);
}
public ref struct Enumerator : IEnumerator<byte>
{
private HybridLayerSegregatedFitAllocationHandle _handle;
private uint _index = ~0u;
internal Enumerator(HybridLayerSegregatedFitAllocationHandle span)
{
_handle = span;
Span = span.BufferHandle;
}
public ref byte RefCurrent => ref _handle[_index];
public Span<byte> Span { get; init; }
public readonly void Dispose() { }
public bool MoveNext()
{
_index++;
return _index < _handle.AllocSize;
}
public void Reset()
{
_index = 0;
}
object IEnumerator.Current => RefCurrent;
byte IEnumerator<byte>.Current => RefCurrent;
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HLSFAllocationHandle"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Allocator = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocator;
namespace StgSharp.Mathematics.Memory
{
public readonly unsafe struct HybridLayerSegregatedFitAllocationHandle
{
internal readonly Allocator.Entry* EntryHandle;
public readonly long AllocSize;
internal HybridLayerSegregatedFitAllocationHandle(Allocator.Entry* handle, long s)
{
EntryHandle = handle;
Pointer = (byte*)handle->Position;
AllocSize = s;
// Console.WriteLine($"{(ulong)handle->PreviousNear}<->{(ulong)handle->NextNear},\t{handle->Size}");
}
public readonly ref byte this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, AllocSize);
return ref Pointer[index];
}
}
public readonly byte* Pointer { get; init; }
public readonly Span<byte> BufferHandle
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(Pointer, (int)AllocSize);
}
public readonly Enumerator GetEnumerator()
{
return new Enumerator(this);
}
public ref struct Enumerator : IEnumerator<byte>
{
private HybridLayerSegregatedFitAllocationHandle _handle;
private uint _index = ~0u;
internal Enumerator(HybridLayerSegregatedFitAllocationHandle span)
{
_handle = span;
Span = span.BufferHandle;
}
public ref byte RefCurrent => ref _handle[_index];
public Span<byte> Span { get; init; }
public readonly void Dispose() { }
public bool MoveNext()
{
_index++;
return _index < _handle.AllocSize;
}
public void Reset()
{
_index = 0;
}
object IEnumerator.Current => RefCurrent;
byte IEnumerator<byte>.Current => RefCurrent;
}
}
}
@@ -1,234 +1,234 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HLSFAllocator.Alloc"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Numerics;
using hlsfHandle = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocationHandle;
namespace StgSharp.Mathematics.Memory
{
public unsafe partial class HybridLayerSegregatedFitAllocator
{
public hlsfHandle Alloc(long size)
{
if (size > _maxSize) {
throw new InvalidOperationException("Requested size exceeds maximum allocatable size.");
}
int level = GetLevelFromSize(size);
int segmentIndex = DetermineSegmentIndex(_levelSizeArray, size, level);
int i = level, s = segmentIndex;
Entry* handle;
while (!TryPopLevelForAllocation(i, s, out handle))
{
if (++s >= 3)
{
s = 0;
i++;
}
if (i > MaxLevel)
{
if (TryPopLevelForAllocation(10, 1, out handle))
{
break;
} else if (TryAllocateNew32MBBlock(out handle))
{
break;
}
throw new OverflowException("Out of memory: Unable to allocate new 16MB block or find suitable bucket for allocation.");
}
}
if (handle != null)
{
int requestedOffset = (segmentIndex + 1) * _levelSizeArray[level];
if (requestedOffset > handle->Size) {
throw new OverflowException("Out of memory: insufficient block size returned from spare memory.");
}
long remainder = handle->Size - requestedOffset;
SetEntryLevel(handle, handle->Position, (uint)requestedOffset);
handle->State = EntryState.Alloc;
if (remainder > 0) {
SliceMemory(handle->NextNear, remainder);
}
}
return new hlsfHandle(handle, size);
}
/// <summary>
/// Slice the memory block into smaller blocks and add them to the appropriate buckets
/// Notice: this method is not responsible for freeing the original BucketNode
/// </summary>
/// <param name="e">
///
/// </param>
/// <param name="sizeToSlice">
///
/// </param>
private void SliceMemory(Entry* e, long sizeToSlice)
{
Entry* next = e;
long remain = sizeToSlice; // remained size to slice
long offset = (long)e->Position - sizeToSlice - (long)m_Buffer; // offset of buffer to slice
long end = (long)e->Position - (long)m_Buffer; // end offset of buffer to slice
int i = (BitOperations.TrailingZeroCount(offset) - 6) / 2;
i = int.Min(10, i);
for (; i < _levelSizeArray.Length; i++)
{
int size = _levelSizeArray[i];
int upper = size * 4;
long edge = (offset + upper - 1) / upper * upper;
int count = (int)((long.Min(edge, end) - offset) / size);
if (count > 0)
{
long newSize = count * size;
Entry* newEntry = (Entry*)_entries.Allocate();
InsertEntryBefore(newEntry, next);
SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
newEntry->State = EntryState.Empty;
BucketNode* b = (BucketNode*)newEntry->Position;
b->EntryRef = newEntry;
PushBucketToLevel(i, count - 1, b);
offset += newSize;
remain -= newSize;
}
if (remain <= 0) {
return;
}
}
if (i > MaxLevel)
{
int size = _levelSizeArray[^1];
int upper = size * 4;
long edge = end / upper * upper;
int count = (int)((long.Min(edge, end) - offset) / upper);
if (count > 0)
{
long newSize = (long)count * (long)upper;
Entry* newEntry = (Entry*)_entries.Allocate();
InsertEntryBefore(newEntry, next);
SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
newEntry->State = EntryState.Empty;
BucketNode* b = (BucketNode*)newEntry->Position;
b->EntryRef = newEntry;
PushBucketToLevel(i, count - 1, b);
offset += newSize;
remain -= newSize;
if (remain <= 0) {
return;
}
}
}
for (i--; i >= 0; i--)
{
int size = _levelSizeArray[i];
long edge = end / size * size;
int count = (int)((long.Min(edge, end) - offset) / size);
if (count > 0)
{
long newSize = count * size;
Entry* newEntry = (Entry*)_entries.Allocate();
InsertEntryBefore(newEntry, next);
SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
newEntry->State = EntryState.Empty;
BucketNode* b = (BucketNode*)newEntry->Position;
b->EntryRef = newEntry;
b->NextLevel = null;
b->PreviousLevel = null;
PushBucketToLevel(i, count - 1, b);
offset += newSize;
remain -= newSize;
}
if (remain <= 0) {
return;
}
}
}
/// <summary>
/// Try to allocate a new 16MB block when all buckets are empty
/// </summary>
/// <param name="handle">
/// Output handle to the allocated Entry
/// </param>
/// <returns>
/// True if allocation succeeded
/// </returns>
private bool TryAllocateNew32MBBlock(out Entry* handle)
{
nuint pos = _spareMemory->Position;
long remain = _spareMemory->Size;
if (remain <= 0)
{
handle = null;
return false;
}
long size = remain > 32 * 1024 * 1024L ? 32 * 1024 * 1024L : remain;
// build new entry
Entry* e = (Entry*)_entries.Allocate();
SetEntryLevel(e, pos, size);
InsertEntryBefore(e, _spareMemory);
e->State = EntryState.Empty;
// set fields of spare memory
_spareMemory->Size -= size;
_spareMemory->Position += (nuint)size;
handle = e;
return true;
}
/// <summary>
/// Helper method for allocation - converts BucketNode to Entry
/// </summary>
private bool TryPopLevelForAllocation(int levelIndex, int sizeIndex, out Entry* entry)
{
if (TryPopLevel(levelIndex, sizeIndex, out BucketNode* bucketNode))
{
entry = bucketNode->EntryRef;
return entry != null;
}
entry = null;
return false;
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HLSFAllocator.Alloc"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Numerics;
using hlsfHandle = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocationHandle;
namespace StgSharp.Mathematics.Memory
{
public unsafe partial class HybridLayerSegregatedFitAllocator
{
public hlsfHandle Alloc(long size)
{
if (size > _maxSize) {
throw new InvalidOperationException("Requested size exceeds maximum allocatable size.");
}
int level = GetLevelFromSize(size);
int segmentIndex = DetermineSegmentIndex(_levelSizeArray, size, level);
int i = level, s = segmentIndex;
Entry* handle;
while (!TryPopLevelForAllocation(i, s, out handle))
{
if (++s >= 3)
{
s = 0;
i++;
}
if (i > MaxLevel)
{
if (TryPopLevelForAllocation(10, 1, out handle))
{
break;
} else if (TryAllocateNew32MBBlock(out handle))
{
break;
}
throw new OverflowException("Out of memory: Unable to allocate new 16MB block or find suitable bucket for allocation.");
}
}
if (handle != null)
{
int requestedOffset = (segmentIndex + 1) * _levelSizeArray[level];
if (requestedOffset > handle->Size) {
throw new OverflowException("Out of memory: insufficient block size returned from spare memory.");
}
long remainder = handle->Size - requestedOffset;
SetEntryLevel(handle, handle->Position, (uint)requestedOffset);
handle->State = EntryState.Alloc;
if (remainder > 0) {
SliceMemory(handle->NextNear, remainder);
}
}
return new hlsfHandle(handle, size);
}
/// <summary>
/// Slice the memory block into smaller blocks and add them to the appropriate buckets
/// Notice: this method is not responsible for freeing the original BucketNode
/// </summary>
/// <param name="e">
///
/// </param>
/// <param name="sizeToSlice">
///
/// </param>
private void SliceMemory(Entry* e, long sizeToSlice)
{
Entry* next = e;
long remain = sizeToSlice; // remained size to slice
long offset = (long)e->Position - sizeToSlice - (long)m_Buffer; // offset of buffer to slice
long end = (long)e->Position - (long)m_Buffer; // end offset of buffer to slice
int i = (BitOperations.TrailingZeroCount(offset) - 6) / 2;
i = int.Min(10, i);
for (; i < _levelSizeArray.Length; i++)
{
int size = _levelSizeArray[i];
int upper = size * 4;
long edge = (offset + upper - 1) / upper * upper;
int count = (int)((long.Min(edge, end) - offset) / size);
if (count > 0)
{
long newSize = count * size;
Entry* newEntry = (Entry*)_entries.Allocate();
InsertEntryBefore(newEntry, next);
SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
newEntry->State = EntryState.Empty;
BucketNode* b = (BucketNode*)newEntry->Position;
b->EntryRef = newEntry;
PushBucketToLevel(i, count - 1, b);
offset += newSize;
remain -= newSize;
}
if (remain <= 0) {
return;
}
}
if (i > MaxLevel)
{
int size = _levelSizeArray[^1];
int upper = size * 4;
long edge = end / upper * upper;
int count = (int)((long.Min(edge, end) - offset) / upper);
if (count > 0)
{
long newSize = (long)count * (long)upper;
Entry* newEntry = (Entry*)_entries.Allocate();
InsertEntryBefore(newEntry, next);
SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
newEntry->State = EntryState.Empty;
BucketNode* b = (BucketNode*)newEntry->Position;
b->EntryRef = newEntry;
PushBucketToLevel(i, count - 1, b);
offset += newSize;
remain -= newSize;
if (remain <= 0) {
return;
}
}
}
for (i--; i >= 0; i--)
{
int size = _levelSizeArray[i];
long edge = end / size * size;
int count = (int)((long.Min(edge, end) - offset) / size);
if (count > 0)
{
long newSize = count * size;
Entry* newEntry = (Entry*)_entries.Allocate();
InsertEntryBefore(newEntry, next);
SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
newEntry->State = EntryState.Empty;
BucketNode* b = (BucketNode*)newEntry->Position;
b->EntryRef = newEntry;
b->NextLevel = null;
b->PreviousLevel = null;
PushBucketToLevel(i, count - 1, b);
offset += newSize;
remain -= newSize;
}
if (remain <= 0) {
return;
}
}
}
/// <summary>
/// Try to allocate a new 16MB block when all buckets are empty
/// </summary>
/// <param name="handle">
/// Output handle to the allocated Entry
/// </param>
/// <returns>
/// True if allocation succeeded
/// </returns>
private bool TryAllocateNew32MBBlock(out Entry* handle)
{
nuint pos = _spareMemory->Position;
long remain = _spareMemory->Size;
if (remain <= 0)
{
handle = null;
return false;
}
long size = remain > 32 * 1024 * 1024L ? 32 * 1024 * 1024L : remain;
// build new entry
Entry* e = (Entry*)_entries.Allocate();
SetEntryLevel(e, pos, size);
InsertEntryBefore(e, _spareMemory);
e->State = EntryState.Empty;
// set fields of spare memory
_spareMemory->Size -= size;
_spareMemory->Position += (nuint)size;
handle = e;
return true;
}
/// <summary>
/// Helper method for allocation - converts BucketNode to Entry
/// </summary>
private bool TryPopLevelForAllocation(int levelIndex, int sizeIndex, out Entry* entry)
{
if (TryPopLevel(levelIndex, sizeIndex, out BucketNode* bucketNode))
{
entry = bucketNode->EntryRef;
return entry != null;
}
entry = null;
return false;
}
}
}
@@ -1,171 +1,171 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HLSFAllocator.BucketNode"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace StgSharp.Mathematics.Memory
{
public unsafe partial class HybridLayerSegregatedFitAllocator
{
private void PushBucketToLevel(int levelIndex, int sizeIndex, BucketNode* node)
{
if (node == null || node->EntryRef == null) {
return;
}
Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex;
if (_bucketHeads[index] is null)
{
_bucketHeads[index] = node;
node->IsInBucket = true;
node->NextLevel = null;
node->PreviousLevel = null;
// Console.WriteLine($"Push to empty level of {levelIndex} {sizeIndex}, bucket pos {(ulong)node}");
return;
} else
{
BucketNode* head = _bucketHeads[index];
node->NextLevel = head;
BucketNode* headNode = head;
node->IsInBucket = true;
_bucketHeads[index] = node;
// Console.WriteLine($"Push to head of level of {levelIndex} {sizeIndex}, bucket pos {(ulong)node}, previous head is at {head}");
if (headNode != null)
{
headNode->PreviousLevel = node;
}
return;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void RemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
{
Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex;
if (_bucketHeads[index] == node)
{
if (TryPopLevel(levelIndex, sizeIndex, out _)) {
return;
}
}
BucketNode* previous = node->PreviousLevel;
BucketNode* next = node->NextLevel;
if (previous != null) {
previous->NextLevel = next;
}
if (next != null) {
next->PreviousLevel = previous;
}
node->NextLevel = (BucketNode*)null;
node->PreviousLevel = (BucketNode*)null;
node->IsInBucket = false;
return;
}
private bool TryPopLevel(int levelIndex, int sizeIndex, out BucketNode* node)
{
Index index = levelIndex > MaxLevel ? ^1 : (3 * levelIndex) + sizeIndex;
BucketNode* head = _bucketHeads[index];
if (head == null)
{
node = null;
return false;
}
// Console.WriteLine($"level popped from level {levelIndex} {sizeIndex}, node is at {(ulong)head}");
BucketNode* next = head->NextLevel;
if (next == null)
{
_bucketHeads[index] = null;
node = head;
return true;
}
_bucketHeads[index] = head->NextLevel;
node = head;
node->IsInBucket = false;
node->NextLevel = null;
if (next != null) {
next->PreviousLevel = null;
}
return true;
}
private bool TryRemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
{
Index index = levelIndex > 9 ? ^1 : (3 * levelIndex) + sizeIndex;
if (_bucketHeads[index] == node)
{
bool ok = TryPopLevel(levelIndex, sizeIndex, out _);
return ok;
}
BucketNode* previous = node->PreviousLevel;
BucketNode* next = node->NextLevel;
if (previous != null) {
previous->NextLevel = next;
}
if (next != null) {
next->PreviousLevel = previous;
}
node->NextLevel = null;
node->PreviousLevel = null;
node->IsInBucket = false;
node->EntryRef = null;
return true;
}
/// <summary>
/// BucketNode structure - specialized node for bucket linked lists Stored in main
/// allocation area (user-overwritable region)
/// </summary>
[StructLayout(LayoutKind.Explicit)]
internal struct BucketNode
{
[FieldOffset(0)] internal Entry* EntryRef; // Pointer to corresponding Entry
// [FieldOffset(16)] internal ulong LockBuffer;
[FieldOffset(8)] internal BucketNode* NextLevel; // Bucket stack linked list: next pointer
[FieldOffset(24)] internal BucketNode* PreviousLevel; // Bucket stack linked list: previous pointer
[FieldOffset(32)] internal bool IsInBucket; // Whether in bucket
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HLSFAllocator.BucketNode"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace StgSharp.Mathematics.Memory
{
public unsafe partial class HybridLayerSegregatedFitAllocator
{
private void PushBucketToLevel(int levelIndex, int sizeIndex, BucketNode* node)
{
if (node == null || node->EntryRef == null) {
return;
}
Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex;
if (_bucketHeads[index] is null)
{
_bucketHeads[index] = node;
node->IsInBucket = true;
node->NextLevel = null;
node->PreviousLevel = null;
// Console.WriteLine($"Push to empty level of {levelIndex} {sizeIndex}, bucket pos {(ulong)node}");
return;
} else
{
BucketNode* head = _bucketHeads[index];
node->NextLevel = head;
BucketNode* headNode = head;
node->IsInBucket = true;
_bucketHeads[index] = node;
// Console.WriteLine($"Push to head of level of {levelIndex} {sizeIndex}, bucket pos {(ulong)node}, previous head is at {head}");
if (headNode != null)
{
headNode->PreviousLevel = node;
}
return;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void RemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
{
Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex;
if (_bucketHeads[index] == node)
{
if (TryPopLevel(levelIndex, sizeIndex, out _)) {
return;
}
}
BucketNode* previous = node->PreviousLevel;
BucketNode* next = node->NextLevel;
if (previous != null) {
previous->NextLevel = next;
}
if (next != null) {
next->PreviousLevel = previous;
}
node->NextLevel = (BucketNode*)null;
node->PreviousLevel = (BucketNode*)null;
node->IsInBucket = false;
return;
}
private bool TryPopLevel(int levelIndex, int sizeIndex, out BucketNode* node)
{
Index index = levelIndex > MaxLevel ? ^1 : (3 * levelIndex) + sizeIndex;
BucketNode* head = _bucketHeads[index];
if (head == null)
{
node = null;
return false;
}
// Console.WriteLine($"level popped from level {levelIndex} {sizeIndex}, node is at {(ulong)head}");
BucketNode* next = head->NextLevel;
if (next == null)
{
_bucketHeads[index] = null;
node = head;
return true;
}
_bucketHeads[index] = head->NextLevel;
node = head;
node->IsInBucket = false;
node->NextLevel = null;
if (next != null) {
next->PreviousLevel = null;
}
return true;
}
private bool TryRemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
{
Index index = levelIndex > 9 ? ^1 : (3 * levelIndex) + sizeIndex;
if (_bucketHeads[index] == node)
{
bool ok = TryPopLevel(levelIndex, sizeIndex, out _);
return ok;
}
BucketNode* previous = node->PreviousLevel;
BucketNode* next = node->NextLevel;
if (previous != null) {
previous->NextLevel = next;
}
if (next != null) {
next->PreviousLevel = previous;
}
node->NextLevel = null;
node->PreviousLevel = null;
node->IsInBucket = false;
node->EntryRef = null;
return true;
}
/// <summary>
/// BucketNode structure - specialized node for bucket linked lists Stored in main
/// allocation area (user-overwritable region)
/// </summary>
[StructLayout(LayoutKind.Explicit)]
internal struct BucketNode
{
[FieldOffset(0)] internal Entry* EntryRef; // Pointer to corresponding Entry
// [FieldOffset(16)] internal ulong LockBuffer;
[FieldOffset(8)] internal BucketNode* NextLevel; // Bucket stack linked list: next pointer
[FieldOffset(24)] internal BucketNode* PreviousLevel; // Bucket stack linked list: previous pointer
[FieldOffset(32)] internal bool IsInBucket; // Whether in bucket
}
}
}
@@ -1,122 +1,122 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HLSFAllocator.Debug"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Mathematics.Memory
{
public sealed class HLSFPositionChainBreakException : Exception
{
public HLSFPositionChainBreakException() : base("Position chain of an HLSF allocator is broken") { }
}
public unsafe partial class HybridLayerSegregatedFitAllocator
{
private object _lock = new object();
public void DisplayEntryLayout(bool singleStep)
{
lock (_lock)
{
Entry* cur = _spareMemory;
while (cur != null)
{
Console.WriteLine(EntryToString(cur));
cur = cur->PreviousNear;
if (singleStep) {
Console.ReadKey();
}
}
}
}
public void DumpPositionChain(bool singleStep = false, int maxCount = 4096)
{
lock (_lock)
{
try
{
Entry* cur = _spareMemory;
int cnt = 0;
if (cur != null) {
cur = cur->NextNear;
}
Console.WriteLine(EntryToString(_spareMemory));
while (cur != null && cur != _spareMemory && cnt < maxCount)
{
Console.WriteLine(EntryToString(cur));
cnt++;
if (singleStep) {
Console.ReadKey();
}
cur = cur->NextNear;
}
if (cur == _spareMemory)
{
Console.WriteLine("(reached sentinel)");
} else if (cnt >= maxCount) {
Console.WriteLine("(stopped at maxCount)");
}
}
catch (Exception ex)
{
Console.WriteLine($"[DumpPositionChain-forward] Exception: {ex.Message}");
}
}
}
public IEnumerable<string> ShowEntryLayout()
{
lock (_lock)
{
Stack<string> layout = [];
Entry* cur = _spareMemory;
while (cur != null)
{
layout.Push(EntryToString(cur));
cur = cur->PreviousNear;
}
return layout;
}
}
private static string EntryToString(Entry* e)
{
BucketNode* b = (BucketNode*)e->Position;
return $"{(ulong)e->PreviousNear}<-prev- Position:{(ulong)e} Bucket:{{{(ulong)e->Position},{e->Level},{e->Size}}} -next->{(ulong)e->NextNear}";
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HLSFAllocator.Debug"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Mathematics.Memory
{
public sealed class HLSFPositionChainBreakException : Exception
{
public HLSFPositionChainBreakException() : base("Position chain of an HLSF allocator is broken") { }
}
public unsafe partial class HybridLayerSegregatedFitAllocator
{
private object _lock = new object();
public void DisplayEntryLayout(bool singleStep)
{
lock (_lock)
{
Entry* cur = _spareMemory;
while (cur != null)
{
Console.WriteLine(EntryToString(cur));
cur = cur->PreviousNear;
if (singleStep) {
Console.ReadKey();
}
}
}
}
public void DumpPositionChain(bool singleStep = false, int maxCount = 4096)
{
lock (_lock)
{
try
{
Entry* cur = _spareMemory;
int cnt = 0;
if (cur != null) {
cur = cur->NextNear;
}
Console.WriteLine(EntryToString(_spareMemory));
while (cur != null && cur != _spareMemory && cnt < maxCount)
{
Console.WriteLine(EntryToString(cur));
cnt++;
if (singleStep) {
Console.ReadKey();
}
cur = cur->NextNear;
}
if (cur == _spareMemory)
{
Console.WriteLine("(reached sentinel)");
} else if (cnt >= maxCount) {
Console.WriteLine("(stopped at maxCount)");
}
}
catch (Exception ex)
{
Console.WriteLine($"[DumpPositionChain-forward] Exception: {ex.Message}");
}
}
}
public IEnumerable<string> ShowEntryLayout()
{
lock (_lock)
{
Stack<string> layout = [];
Entry* cur = _spareMemory;
while (cur != null)
{
layout.Push(EntryToString(cur));
cur = cur->PreviousNear;
}
return layout;
}
}
private static string EntryToString(Entry* e)
{
BucketNode* b = (BucketNode*)e->Position;
return $"{(ulong)e->PreviousNear}<-prev- Position:{(ulong)e} Bucket:{{{(ulong)e->Position},{e->Level},{e->Size}}} -next->{(ulong)e->NextNear}";
}
}
}
@@ -1,129 +1,129 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HLSFAllocator.Free"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using hlsfHandle = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocationHandle;
namespace StgSharp.Mathematics.Memory
{
public enum FreePolicy
{
FullCollect,
LazyCollect,
NoCollect
}
public unsafe partial class HybridLayerSegregatedFitAllocator
{
/// <summary>
/// Minimal collect: linear scan, merge contiguous empty Blocks into one, push merged head
/// back to bucket. Caller-responsible design: no slicing or redistribution.
/// </summary>
public void Collect()
{
Entry* cur = _spareMemory->NextNear;
long size = 0;
while (cur != null && cur != _spareMemory)
{
// cur is not empty, skip
if (cur->State != EntryState.Empty)
{
cur = cur->NextNear;
continue;
}
// remove cur from bucket
do
{
size += cur->Size;
Entry* next = cur->NextNear;
RemoveFromBucket(cur->Level, DetermineSegmentIndex(_levelSizeArray, cur->Size, cur->Level), (BucketNode*)cur->Position);
RemoveFromPositionChain(cur);
cur = next;
} while (cur != null && cur != _spareMemory && cur->State == EntryState.Empty);
if (size > 0) {
SliceMemory(cur, size);
}
}
}
/// <summary>
/// Free allocated memory block and perform adjacent block merging
/// </summary>
/// <param name="handle">
/// Handle to the allocated memory block
/// <param name="collectPolicy">
/// </param>
public void Free(hlsfHandle handle, FreePolicy collectPolicy = FreePolicy.NoCollect)
{
Entry* e = handle.EntryHandle;
if (e == null) {
return;
}
if (e->State != EntryState.Alloc) {
return;
}
// Mark as empty so it can be merged
e->State = EntryState.Empty;
// Try merge with adjacent free blocks and remove them from buckets
if (collectPolicy == FreePolicy.LazyCollect)
{
MergeAndRemoveFromBuckets(e);
}
// Calculate final level and segment for the merged block
long finalSize = e->Size;
int finalLevel = GetLevelFromSize(finalSize);
e->Level = finalLevel;
int finalSegment = DetermineSegmentIndex(_levelSizeArray, finalSize, finalLevel);
// Push the merged entry back to appropriate bucket
PushBucketToLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
// Console.WriteLine("one block freed");
if (collectPolicy == FreePolicy.FullCollect)
{
Collect();
}
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HLSFAllocator.Free"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using hlsfHandle = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocationHandle;
namespace StgSharp.Mathematics.Memory
{
public enum FreePolicy
{
FullCollect,
LazyCollect,
NoCollect
}
public unsafe partial class HybridLayerSegregatedFitAllocator
{
/// <summary>
/// Minimal collect: linear scan, merge contiguous empty Blocks into one, push merged head
/// back to bucket. Caller-responsible design: no slicing or redistribution.
/// </summary>
public void Collect()
{
Entry* cur = _spareMemory->NextNear;
long size = 0;
while (cur != null && cur != _spareMemory)
{
// cur is not empty, skip
if (cur->State != EntryState.Empty)
{
cur = cur->NextNear;
continue;
}
// remove cur from bucket
do
{
size += cur->Size;
Entry* next = cur->NextNear;
RemoveFromBucket(cur->Level, DetermineSegmentIndex(_levelSizeArray, cur->Size, cur->Level), (BucketNode*)cur->Position);
RemoveFromPositionChain(cur);
cur = next;
} while (cur != null && cur != _spareMemory && cur->State == EntryState.Empty);
if (size > 0) {
SliceMemory(cur, size);
}
}
}
/// <summary>
/// Free allocated memory block and perform adjacent block merging
/// </summary>
/// <param name="handle">
/// Handle to the allocated memory block
/// <param name="collectPolicy">
/// </param>
public void Free(hlsfHandle handle, FreePolicy collectPolicy = FreePolicy.NoCollect)
{
Entry* e = handle.EntryHandle;
if (e == null) {
return;
}
if (e->State != EntryState.Alloc) {
return;
}
// Mark as empty so it can be merged
e->State = EntryState.Empty;
// Try merge with adjacent free blocks and remove them from buckets
if (collectPolicy == FreePolicy.LazyCollect)
{
MergeAndRemoveFromBuckets(e);
}
// Calculate final level and segment for the merged block
long finalSize = e->Size;
int finalLevel = GetLevelFromSize(finalSize);
e->Level = finalLevel;
int finalSegment = DetermineSegmentIndex(_levelSizeArray, finalSize, finalLevel);
// Push the merged entry back to appropriate bucket
PushBucketToLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
// Console.WriteLine("one block freed");
if (collectPolicy == FreePolicy.FullCollect)
{
Collect();
}
}
}
}
@@ -1,92 +1,92 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HLSFAllocator.Level"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Mathematics.Memory
{
public unsafe partial class HybridLayerSegregatedFitAllocator
{
private const int MaxLevel = 9;
private int[] _levelSizeArray = [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216];
public int AlignOfLevel(int bucketLevel)
{
int blockSize = _levelSizeArray[bucketLevel];
return int.Min(blockSize, _align);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetLevelFromSize(long size)
{
/*
* Level sizes: [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, ......]
* Pattern: 64 * 4^level = 2^6 * 2^(2*level) = 2^(6+2*level)
*
* For size S, we need: 2^(6+2*level) >= S
* So: 6+2*level >= log2(S)
* Therefore: level >= (log2(S) - 6) / 2
*
* We use hardware LZCNT instruction via BitOperations.LeadingZeroCount
* log2(S) = 63 - LeadingZeroCount(S) for 64-bit values
*/
int log2Size = 63 - BitOperations.LeadingZeroCount((ulong)size);
// Calculate level: level = max(0, (log2Size - 6) / 2)
int level = Math.Max(0, (log2Size - 6) / 2);
// caller is responsible for ensuring level is within bounds
return level;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetLevelFromSize(uint size)
{
return GetLevelFromSize(size);
}
}
public enum CommonMemoryAlign : int
{
XMMLevel = 16,
YMMLevel = 32,
ZMMLevel = 64,
ArmNEONLevel = 16,
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HLSFAllocator.Level"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Mathematics.Memory
{
public unsafe partial class HybridLayerSegregatedFitAllocator
{
private const int MaxLevel = 9;
private int[] _levelSizeArray = [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216];
public int AlignOfLevel(int bucketLevel)
{
int blockSize = _levelSizeArray[bucketLevel];
return int.Min(blockSize, _align);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetLevelFromSize(long size)
{
/*
* Level sizes: [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, ......]
* Pattern: 64 * 4^level = 2^6 * 2^(2*level) = 2^(6+2*level)
*
* For size S, we need: 2^(6+2*level) >= S
* So: 6+2*level >= log2(S)
* Therefore: level >= (log2(S) - 6) / 2
*
* We use hardware LZCNT instruction via BitOperations.LeadingZeroCount
* log2(S) = 63 - LeadingZeroCount(S) for 64-bit values
*/
int log2Size = 63 - BitOperations.LeadingZeroCount((ulong)size);
// Calculate level: level = max(0, (log2Size - 6) / 2)
int level = Math.Max(0, (log2Size - 6) / 2);
// caller is responsible for ensuring level is within bounds
return level;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetLevelFromSize(uint size)
{
return GetLevelFromSize(size);
}
}
public enum CommonMemoryAlign : int
{
XMMLevel = 16,
YMMLevel = 32,
ZMMLevel = 64,
ArmNEONLevel = 16,
}
}
@@ -1,208 +1,208 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HLSFAllocator.SegmentEntry"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace StgSharp.Mathematics.Memory
{
public unsafe partial class HybridLayerSegregatedFitAllocator
{
/// <summary>
/// Check if two Entries are adjacent in memory
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool AssertAdjacentTo(Entry* current, Entry* other)
{
// Only a predicate; never throw here. Callers rely on false to skip merging.
return other->Position + (nuint)other->Size == current->Position ||
current->Position + (nuint)current->Size == other->Position;
}
/// <summary>
/// check out if a bucket is within the same higher-level boundary as currentOffset
/// </summary>
private bool AssertBoundary(Entry* previous, Entry* current, int level)
{
level = int.Min(level, MaxLevel);
int nextLevelSize = _levelSizeArray[level] * 4;
ulong prevHead = previous->Position - (ulong)m_Buffer,
currHead = current->Position - (ulong)m_Buffer;
return (currHead / (ulong)nextLevelSize) == (prevHead / (ulong)nextLevelSize);
}
/// <summary>
/// Determine segment index based on size and originLevel
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int DetermineSegmentIndex(int[] levelSizeArray, long size, int level)
{
level = int.Min(level, MaxLevel);
return (int)((size - 1) / levelSizeArray[level]);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void InsertEntryAfter(Entry* entry, Entry* position)
{
Entry* next = position->NextNear;
entry->NextNear = next;
entry->PreviousNear = position;
next->PreviousNear = entry;
position->NextNear = entry;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void InsertEntryBefore(Entry* entry, Entry* position)
{
Entry* prev = position->PreviousNear;
entry->NextNear = position;
entry->PreviousNear = prev;
prev->NextNear = entry;
position->PreviousNear = entry;
}
/// <summary>
/// Used in Free operations, efficiently remove from buckets and merge adjacent blocks.
/// Current Entry will serve as the final merged block. The Merge will be in lazy
/// strategy.
/// </summary>
private void MergeAndRemoveFromBuckets(Entry* entry)
{
if (entry == null) {
return;
}
long size = entry->Size;
int originLevel = entry->Level;
int segment = DetermineSegmentIndex(_levelSizeArray, size, originLevel);
do
{
// previous direction merge
Entry* p = entry->PreviousNear;
if (p == null ||
p == _spareMemory ||
p->State != EntryState.Empty ||
p->Level != entry->Level ||
!AssertBoundary(p, entry, originLevel))
{
break;
}
// Console.WriteLine($"{(ulong)p->Size} merged to {(ulong)entry->Size} previous");
entry->Size += p->Size;
entry->Position = p->Position;
RemoveFromBucket(originLevel, DetermineSegmentIndex(_levelSizeArray, p->Size, originLevel), (BucketNode*)p->Position);
RemoveFromPositionChain(p);
} while (true);
do
{
// next direction merge
Entry* n = entry->NextNear;
if (n == null ||
n == _spareMemory ||
n->State != EntryState.Empty ||
n->Level != entry->Level ||
!AssertBoundary(n, entry, originLevel))
{
break;
}
// Console.WriteLine($"{(ulong)n->Size} merged to {(ulong)entry->Size} next");
entry->Size += n->Size;
RemoveFromBucket(originLevel, DetermineSegmentIndex(_levelSizeArray, n->Size, originLevel), (BucketNode*)n->Position);
RemoveFromPositionChain(n);
} while (true);
originLevel = int.Min(9, originLevel);
entry->Level += entry->Size >= _levelSizeArray[originLevel] * 4 ? 1 : 0;
entry->Level &= 15;
BucketNode* bucket = (BucketNode*)entry->Position;
bucket->EntryRef = entry;
}
/// <summary>
/// Remove node from position linked list, this method does not recycle a BucketNode from
/// size bucket
/// </summary>
private void RemoveFromPositionChain(Entry* entry)
{
Entry* prev = entry->PreviousNear;
Entry* next = entry->NextNear;
prev->NextNear = next;
next->PreviousNear = prev;
// Clear node pointers
entry->NextNear = null;
entry->PreviousNear = null;
// Console.WriteLine("entry recycled.");
_entries.Free((nuint)entry);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void SetEntryLevel(Entry* entry, nuint position, long size)
{
entry ->Position = position;
entry->Size = size;
entry->Level = GetLevelFromSize(size);
}
/// <summary>
/// Entry structure - streamlined version (only position and state information) Stored in
/// external SLAB
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 40)]
internal struct Entry
{
[FieldOffset(0)] internal Entry* NextNear; // Position linked list: next pointer
[FieldOffset(8)] internal Entry* PreviousNear; // Position linked list: previous pointer
[FieldOffset(24)] internal int Level; // Level of the block in the allocator
[FieldOffset(16)] internal long Size; // Block size information
[FieldOffset(36)] public EntryState State;
[FieldOffset(28)] public nuint Position; // Memory block position
}
internal enum EntryState : uint
{
Spare,
Empty,
Alloc,
Large
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HLSFAllocator.SegmentEntry"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace StgSharp.Mathematics.Memory
{
public unsafe partial class HybridLayerSegregatedFitAllocator
{
/// <summary>
/// Check if two Entries are adjacent in memory
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool AssertAdjacentTo(Entry* current, Entry* other)
{
// Only a predicate; never throw here. Callers rely on false to skip merging.
return other->Position + (nuint)other->Size == current->Position ||
current->Position + (nuint)current->Size == other->Position;
}
/// <summary>
/// check out if a bucket is within the same higher-level boundary as currentOffset
/// </summary>
private bool AssertBoundary(Entry* previous, Entry* current, int level)
{
level = int.Min(level, MaxLevel);
int nextLevelSize = _levelSizeArray[level] * 4;
ulong prevHead = previous->Position - (ulong)m_Buffer,
currHead = current->Position - (ulong)m_Buffer;
return (currHead / (ulong)nextLevelSize) == (prevHead / (ulong)nextLevelSize);
}
/// <summary>
/// Determine segment index based on size and originLevel
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int DetermineSegmentIndex(int[] levelSizeArray, long size, int level)
{
level = int.Min(level, MaxLevel);
return (int)((size - 1) / levelSizeArray[level]);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void InsertEntryAfter(Entry* entry, Entry* position)
{
Entry* next = position->NextNear;
entry->NextNear = next;
entry->PreviousNear = position;
next->PreviousNear = entry;
position->NextNear = entry;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void InsertEntryBefore(Entry* entry, Entry* position)
{
Entry* prev = position->PreviousNear;
entry->NextNear = position;
entry->PreviousNear = prev;
prev->NextNear = entry;
position->PreviousNear = entry;
}
/// <summary>
/// Used in Free operations, efficiently remove from buckets and merge adjacent blocks.
/// Current Entry will serve as the final merged block. The Merge will be in lazy
/// strategy.
/// </summary>
private void MergeAndRemoveFromBuckets(Entry* entry)
{
if (entry == null) {
return;
}
long size = entry->Size;
int originLevel = entry->Level;
int segment = DetermineSegmentIndex(_levelSizeArray, size, originLevel);
do
{
// previous direction merge
Entry* p = entry->PreviousNear;
if (p == null ||
p == _spareMemory ||
p->State != EntryState.Empty ||
p->Level != entry->Level ||
!AssertBoundary(p, entry, originLevel))
{
break;
}
// Console.WriteLine($"{(ulong)p->Size} merged to {(ulong)entry->Size} previous");
entry->Size += p->Size;
entry->Position = p->Position;
RemoveFromBucket(originLevel, DetermineSegmentIndex(_levelSizeArray, p->Size, originLevel), (BucketNode*)p->Position);
RemoveFromPositionChain(p);
} while (true);
do
{
// next direction merge
Entry* n = entry->NextNear;
if (n == null ||
n == _spareMemory ||
n->State != EntryState.Empty ||
n->Level != entry->Level ||
!AssertBoundary(n, entry, originLevel))
{
break;
}
// Console.WriteLine($"{(ulong)n->Size} merged to {(ulong)entry->Size} next");
entry->Size += n->Size;
RemoveFromBucket(originLevel, DetermineSegmentIndex(_levelSizeArray, n->Size, originLevel), (BucketNode*)n->Position);
RemoveFromPositionChain(n);
} while (true);
originLevel = int.Min(9, originLevel);
entry->Level += entry->Size >= _levelSizeArray[originLevel] * 4 ? 1 : 0;
entry->Level &= 15;
BucketNode* bucket = (BucketNode*)entry->Position;
bucket->EntryRef = entry;
}
/// <summary>
/// Remove node from position linked list, this method does not recycle a BucketNode from
/// size bucket
/// </summary>
private void RemoveFromPositionChain(Entry* entry)
{
Entry* prev = entry->PreviousNear;
Entry* next = entry->NextNear;
prev->NextNear = next;
next->PreviousNear = prev;
// Clear node pointers
entry->NextNear = null;
entry->PreviousNear = null;
// Console.WriteLine("entry recycled.");
_entries.Free((nuint)entry);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void SetEntryLevel(Entry* entry, nuint position, long size)
{
entry ->Position = position;
entry->Size = size;
entry->Level = GetLevelFromSize(size);
}
/// <summary>
/// Entry structure - streamlined version (only position and state information) Stored in
/// external SLAB
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 40)]
internal struct Entry
{
[FieldOffset(0)] internal Entry* NextNear; // Position linked list: next pointer
[FieldOffset(8)] internal Entry* PreviousNear; // Position linked list: previous pointer
[FieldOffset(24)] internal int Level; // Level of the block in the allocator
[FieldOffset(16)] internal long Size; // Block size information
[FieldOffset(36)] public EntryState State;
[FieldOffset(28)] public nuint Position; // Memory block position
}
internal enum EntryState : uint
{
Spare,
Empty,
Alloc,
Large
}
}
}
@@ -1,178 +1,178 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HLSFAllocator"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
namespace StgSharp.Mathematics.Memory
{
public unsafe partial class HybridLayerSegregatedFitAllocator : IDisposable
{
// Bucket system field - directly in main allocator class
private readonly BucketNode *[] _bucketHeads;
// _spareMemory is also the beginning position of allocator
private Entry *_spareMemory;
private readonly byte* m_Buffer;
private bool disposedValue;
private readonly int _align;
private readonly long _maxSize;
private readonly nuint _size;
private readonly SlabAllocator<Entry> _entries;
public HybridLayerSegregatedFitAllocator(nuint byteSize)
{
if (byteSize < 64) {
throw new InvalidOperationException();
}
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
_size = byteSize;
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, 16);
_align = 16;
_bucketHeads = new BucketNode *[_levelSizeArray.Length * 3 + 1];
_spareMemory = (Entry *)_entries.Allocate();
_spareMemory->State = EntryState.Spare;
_spareMemory->PreviousNear = _spareMemory;
_spareMemory->NextNear = _spareMemory;
_spareMemory->Position = (nuint)m_Buffer;
_spareMemory->Size = (long)byteSize;
_maxSize = 32U * 1024U * 1024U;
}
public HybridLayerSegregatedFitAllocator(nuint byteSize, uint maxBlockSize)
{
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, 16);
_align = 16;
_size = byteSize;
_spareMemory = (Entry *)_entries.Allocate();
_spareMemory->State = EntryState.Empty;
_spareMemory->PreviousNear = _spareMemory;
_spareMemory->NextNear = _spareMemory;
_spareMemory->Position = (nuint)m_Buffer;
_spareMemory->Size = (long)byteSize;
int levelCountOrigin = _levelSizeArray.Length;
int levelCount = GetLevelFromSize(maxBlockSize) + 1;
Array.Resize(ref _levelSizeArray, levelCount);
_bucketHeads = new BucketNode *[(levelCount * 3) + 1];
int size = _levelSizeArray[^1];
for (int i = levelCountOrigin; i < levelCount; i++)
{
size *= 4;
_levelSizeArray[i] = size;
}
_maxSize = maxBlockSize;
}
public HybridLayerSegregatedFitAllocator(nuint byteSize, int align)
{
if (align < 16 || (align & (align - 1)) != 0) {
throw new ArgumentException(
"Alignment must be a power of two and at least 16 bytes.");
}
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, (nuint)align);
_align = align;
_size = byteSize;
_bucketHeads = new BucketNode *[_levelSizeArray.Length * 3 + 1];
_spareMemory = (Entry *)_entries.Allocate();
_spareMemory->State = EntryState.Empty;
_spareMemory->PreviousNear = _spareMemory;
_spareMemory->NextNear = _spareMemory;
_spareMemory->Position = (nuint)m_Buffer;
_spareMemory->Size = (long)byteSize;
_maxSize = 32U * 1024U * 1024U;
}
public HybridLayerSegregatedFitAllocator(nuint byteSize, int align, long maxBlockSize)
{
if (align < 16 || (align & (align - 1)) != 0) {
throw new ArgumentException(
"Alignment must be a power of two and at least 16 bytes.");
}
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
m_Buffer = (byte*)NativeMemory.AlignedAlloc(byteSize, (nuint)align);
_align = align;
_size = byteSize;
_spareMemory = (Entry *)_entries.Allocate();
_spareMemory->State = EntryState.Empty;
_spareMemory->PreviousNear = _spareMemory;
_spareMemory->NextNear = _spareMemory;
_spareMemory->Position = (nuint)m_Buffer;
_spareMemory->Size = (long)byteSize;
int levelCountOrigin = _levelSizeArray.Length;
int levelCount = GetLevelFromSize(maxBlockSize) + 1;
Array.Resize(ref _levelSizeArray, levelCount);
_bucketHeads = new BucketNode *[levelCount * 3 + 1];
int size = _levelSizeArray[^1];
for (int i = levelCountOrigin; i < levelCount; i++)
{
size *= 4;
_levelSizeArray[i] = size;
}
_maxSize = maxBlockSize;
}
public void Dispose()
{
Dispose(disposing:true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing) {
_entries.Dispose();
}
NativeMemory.AlignedFree(m_Buffer);
disposedValue = true;
}
}
~HybridLayerSegregatedFitAllocator()
{
Dispose(disposing:false);
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="HLSFAllocator"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
namespace StgSharp.Mathematics.Memory
{
public unsafe partial class HybridLayerSegregatedFitAllocator : IDisposable
{
// Bucket system field - directly in main allocator class
private readonly BucketNode *[] _bucketHeads;
// _spareMemory is also the beginning position of allocator
private Entry *_spareMemory;
private readonly byte* m_Buffer;
private bool disposedValue;
private readonly int _align;
private readonly long _maxSize;
private readonly nuint _size;
private readonly SlabAllocator<Entry> _entries;
public HybridLayerSegregatedFitAllocator(nuint byteSize)
{
if (byteSize < 64) {
throw new InvalidOperationException();
}
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
_size = byteSize;
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, 16);
_align = 16;
_bucketHeads = new BucketNode *[_levelSizeArray.Length * 3 + 1];
_spareMemory = (Entry *)_entries.Allocate();
_spareMemory->State = EntryState.Spare;
_spareMemory->PreviousNear = _spareMemory;
_spareMemory->NextNear = _spareMemory;
_spareMemory->Position = (nuint)m_Buffer;
_spareMemory->Size = (long)byteSize;
_maxSize = 32U * 1024U * 1024U;
}
public HybridLayerSegregatedFitAllocator(nuint byteSize, uint maxBlockSize)
{
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, 16);
_align = 16;
_size = byteSize;
_spareMemory = (Entry *)_entries.Allocate();
_spareMemory->State = EntryState.Empty;
_spareMemory->PreviousNear = _spareMemory;
_spareMemory->NextNear = _spareMemory;
_spareMemory->Position = (nuint)m_Buffer;
_spareMemory->Size = (long)byteSize;
int levelCountOrigin = _levelSizeArray.Length;
int levelCount = GetLevelFromSize(maxBlockSize) + 1;
Array.Resize(ref _levelSizeArray, levelCount);
_bucketHeads = new BucketNode *[(levelCount * 3) + 1];
int size = _levelSizeArray[^1];
for (int i = levelCountOrigin; i < levelCount; i++)
{
size *= 4;
_levelSizeArray[i] = size;
}
_maxSize = maxBlockSize;
}
public HybridLayerSegregatedFitAllocator(nuint byteSize, int align)
{
if (align < 16 || (align & (align - 1)) != 0) {
throw new ArgumentException(
"Alignment must be a power of two and at least 16 bytes.");
}
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, (nuint)align);
_align = align;
_size = byteSize;
_bucketHeads = new BucketNode *[_levelSizeArray.Length * 3 + 1];
_spareMemory = (Entry *)_entries.Allocate();
_spareMemory->State = EntryState.Empty;
_spareMemory->PreviousNear = _spareMemory;
_spareMemory->NextNear = _spareMemory;
_spareMemory->Position = (nuint)m_Buffer;
_spareMemory->Size = (long)byteSize;
_maxSize = 32U * 1024U * 1024U;
}
public HybridLayerSegregatedFitAllocator(nuint byteSize, int align, long maxBlockSize)
{
if (align < 16 || (align & (align - 1)) != 0) {
throw new ArgumentException(
"Alignment must be a power of two and at least 16 bytes.");
}
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
m_Buffer = (byte*)NativeMemory.AlignedAlloc(byteSize, (nuint)align);
_align = align;
_size = byteSize;
_spareMemory = (Entry *)_entries.Allocate();
_spareMemory->State = EntryState.Empty;
_spareMemory->PreviousNear = _spareMemory;
_spareMemory->NextNear = _spareMemory;
_spareMemory->Position = (nuint)m_Buffer;
_spareMemory->Size = (long)byteSize;
int levelCountOrigin = _levelSizeArray.Length;
int levelCount = GetLevelFromSize(maxBlockSize) + 1;
Array.Resize(ref _levelSizeArray, levelCount);
_bucketHeads = new BucketNode *[levelCount * 3 + 1];
int size = _levelSizeArray[^1];
for (int i = levelCountOrigin; i < levelCount; i++)
{
size *= 4;
_levelSizeArray[i] = size;
}
_maxSize = maxBlockSize;
}
public void Dispose()
{
Dispose(disposing:true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing) {
_entries.Dispose();
}
NativeMemory.AlignedFree(m_Buffer);
disposedValue = true;
}
}
~HybridLayerSegregatedFitAllocator()
{
Dispose(disposing:false);
}
}
}
@@ -1,137 +1,137 @@
# HLSF (Hybrid Layer Segregated Fit) Allocator
A constant-time (O(1)) allocator for real-time, high-throughput scenarios. It uses layered size classes with segmented buckets and stores all metadata externally for clean user memory and better cache locality.
## Key Points
- O(1) allocate/free with 10 levels: 64B x 16MB; each level split into 1x/2x/3x segments
- External metadata (`Entry`, `BucketNode`) in dedicated SLABs; user blocks have no headers
- Configurable alignment (>=16B); maximum block size is configurable at construction time via the `maxBlockSize` parameter (default 32MB, 10 levels). The level array and bucket table are dynamically extended when a larger `maxBlockSize` is specified. There is no implicit fallback to `NativeMemory` for oversized requests.
- Single-threaded implementation (external sync required for multi-threaded use)
## Quick Use
```csharp
using var hlsf = new HybridLayerSegregatedFitAllocator(64 * 1024 * 1024);
var h = hlsf.Alloc(1024);
Span<byte> s = h.BufferHandle; s[0] = 42;
hlsf.Free(h);
```
## Core APIs
### Alloc(uint size)
- Purpose: Allocate a block of at least `size` bytes.
- Input: `size` in bytes. Must not exceed the `maxBlockSize` configured at construction time (default 32MB). If `size > _maxSize`, an `InvalidOperationException` is thrown.
- Output: `HybridLayerSegregatedFitAllocationHandle` with pointer access and `Span<byte>`.
- Behavior:
- Classify into level/segment and pop a matching bucket; escalate segments and levels if empty.
- Fallback to overflow bucket or carve 32MB from spare memory if all buckets are empty.
- Mark selected `Entry` as Alloc; slice any remainder into `Empty` entries and push to buckets.
- There is no implicit fallback to `NativeMemory.AlignedAlloc` for oversized requests — all allocations are served from the pre-allocated arena. The level array and bucket table are dynamically sized based on the `maxBlockSize` parameter.
### Free(HybridLayerSegregatedFitAllocationHandle handle)
- Purpose: Free a previously allocated block.
- Behavior:
- Mark the corresponding `Entry` as `Empty`.
- Merge with adjacent `Empty` entries in both directions (boundary and level constraints).
- Remove merged neighbors from buckets, update position chain, and optionally promote level.
- Re-enqueue the final `Entry` into the appropriate bucket.
### Collect()
- Purpose: Reclaim and rebalance free entries without touching allocated blocks.
- Behavior:
- Consolidate fragmented regions where possible and improve bucket coverage.
- Intended to be cheap; safe to call between benchmark phases.
### BenchmarkDotNet (HLSF vs Libc)
Environment: .NET 8.0.23 (Release), x64 RyuJIT AVX2, Windows 11, AMD Ryzen 7 6800H.
| Method | Mean | StdDev | Ratio | Allocated |
|------- |-----:|------:|------:|----------:|
| Hlsf | 838.4 µs | 40.6 µs | 1.00 | 64 B |
| Libc | 849.8 µs | 100.2 µs | 1.01 | 64 B |
Interpretation: HLSF matches libc allocation throughput under the same workload, with slightly tighter dispersion in this run.
> Note: BenchmarkDotNet also warned that the minimum iteration time was under 1ms; for more stable statistics, increase per-iteration work (e.g., higher `AllocationCount`).
## Detailed Design (summary)
-
- Size classes: The default level size array is [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216] (10 levels, 64B × 4^level). When `maxBlockSize` is specified at construction and exceeds the default range, additional levels are appended (each 4× the previous) to cover the requested maximum block size.
- Segments per level: 1x, 2x, 3x
- Buckets: index = 3 * level + segment (overflow bucket for level > 9)
- Metadata: Entry (external SLAB, position chain), BucketNode (user region, bucket chain)
- Position chain: circular doubly linked list anchored by spare sentinel
- Allocation: search buckets by level/segment with escalation; if none, carve 32MB from spare; set Alloc; slice remainder into buckets
- Slicing: align to level boundaries, pack contiguous blocks forward/backward to minimize overhead
- Free/Merge: bidirectional merge under boundary and level constraints; update buckets; optional level promotion
- Alignment: default 16B; internal blocks aligned by slicing/edge calculation
- Complexity: average O(1) with bounded levels and constant-time bucket ops
- `Entry` (external SLAB): linkage in position chain, `Position`, `Size`, `Level`, `State`
- Invariants: near neighbors must form a consistent double-linked ring; `Level` setter validates size vs. level window
- `BucketNode` (in user region): `EntryRef`, `NextLevel`, `PreviousLevel`, `IsInBucket`
- Position chain: circular doubly linked list anchored by sentinel `_spareMemory` (`State.Spare`)
### Allocation Flow
1) Compute `level` and `segment` from requested `size`
2) Try pop bucket (`TryPopLevelForAllocation(level, segment, out entry)`) and escalate:
- Advance `segment` 0��1��2, then `level++`, until `MaxLevel`
- If still empty: try pop from overflow bucket (L=10, seg=1) or `TryAllocateNew32MBBlock`
3) On success: bound-check the slice offset; set `entry` to `Alloc` using `SetEntryLevel`
4) If remainder exists in the source region, call `SliceMemory(entry->NextNear, remainder)` to produce free chunks into buckets
### Slicing Logic (`SliceMemory`)
- Inputs: `next` entry position, `sizeToSlice`
- Compute `offset` and `end` relative to base buffer; pick starting level by trailing-zero count of `offset`:
- `i = (BitOperations.TrailingZeroCount(offset) - 6) / 2`, clamped to `�� MaxLevel`
- Three phases to cover [offset, end):
1) Forward levels i..N-1: for each level size `size` and `upper=size*4`, align to `edge=((offset+upper-1)/upper)*upper`, pack `count = ((min(edge,end) - offset) / size)` blocks
2) If `i > MaxLevel`: pack in largest upper window on tail
3) Backward i-1..0: fill remaining tail with smaller sizes
- For each packed region: allocate new `Entry`, insert before `next`, mark `Empty`, and push corresponding `BucketNode` into the `(level, segment)` bucket (`PushBucketToLevel(i, count-1, b)`)
### Free and Merge (`MergeAndRemoveFromBuckets`)
- Given an `Entry` to free (turned `Empty` by caller):
- Merge left while neighbor `p` is `Empty`, same `Level`, and within boundary (`AssertBoundary`)
- Remove `p` from bucket (`RemoveFromBucket`), expand current, fix `Position`, unlink `p`
- Merge right with neighbor `n` by the same rule
- Optionally promote `Level` if `Size �� levelSizeArray[originLevel] * 4`
- Ensure `BucketNode.EntryRef` points to the merged entry; caller re-enqueues into the proper bucket
### Buckets
- `PushBucketToLevel(level, segment, node)`: push to bucket head, set `IsInBucket`, maintain prev/next
- `TryPopLevel(level, segment, out node)`: pop head; if empty, return false
- `RemoveFromBucket(level, segment, node)`: unlink from middle or use `TryPopLevel` if it is the head
### Large Allocations and Spare Memory
- Requests exceeding the configured `maxBlockSize` are rejected with `InvalidOperationException`. All allocations are served from the pre-allocated arena.
- When all buckets are empty, `TryAllocateNew32MBBlock` carves a region (up to 32MB) from `_spareMemory` to replenish the bucket system, builds an `Empty` entry, and advances the spare pointer
### Alignment
- Default `_align = 16`; `AlignOfLevel(level)` returns `min(levelSizeArray[level], _align)`
- Internal blocks align to level boundaries by design of slicing/edge calculations
### Complexity and Invariants
- Allocate/free are O(1) on average with bounded levels (��10) and constant-time bucket ops
- Invariants guarded by exceptions (e.g., `HLSFPositionChainBreakException`) and pointer consistency checks
## Profiling (hotspots, current workloads)
- SliceMemory ~22% (self ~17%) �� slicing and bucket enqueue dominate allocation path
- MergeAndRemoveFromBuckets ~13% (self ~12%) �� merge + removal dominate free path
- Collect ~7.6% (self ~7.5%) �� low overall cost
# HLSF (Hybrid Layer Segregated Fit) Allocator
A constant-time (O(1)) allocator for real-time, high-throughput scenarios. It uses layered size classes with segmented buckets and stores all metadata externally for clean user memory and better cache locality.
## Key Points
- O(1) allocate/free with 10 levels: 64B x 16MB; each level split into 1x/2x/3x segments
- External metadata (`Entry`, `BucketNode`) in dedicated SLABs; user blocks have no headers
- Configurable alignment (>=16B); maximum block size is configurable at construction time via the `maxBlockSize` parameter (default 32MB, 10 levels). The level array and bucket table are dynamically extended when a larger `maxBlockSize` is specified. There is no implicit fallback to `NativeMemory` for oversized requests.
- Single-threaded implementation (external sync required for multi-threaded use)
## Quick Use
```csharp
using var hlsf = new HybridLayerSegregatedFitAllocator(64 * 1024 * 1024);
var h = hlsf.Alloc(1024);
Span<byte> s = h.BufferHandle; s[0] = 42;
hlsf.Free(h);
```
## Core APIs
### Alloc(uint size)
- Purpose: Allocate a block of at least `size` bytes.
- Input: `size` in bytes. Must not exceed the `maxBlockSize` configured at construction time (default 32MB). If `size > _maxSize`, an `InvalidOperationException` is thrown.
- Output: `HybridLayerSegregatedFitAllocationHandle` with pointer access and `Span<byte>`.
- Behavior:
- Classify into level/segment and pop a matching bucket; escalate segments and levels if empty.
- Fallback to overflow bucket or carve 32MB from spare memory if all buckets are empty.
- Mark selected `Entry` as Alloc; slice any remainder into `Empty` entries and push to buckets.
- There is no implicit fallback to `NativeMemory.AlignedAlloc` for oversized requests — all allocations are served from the pre-allocated arena. The level array and bucket table are dynamically sized based on the `maxBlockSize` parameter.
### Free(HybridLayerSegregatedFitAllocationHandle handle)
- Purpose: Free a previously allocated block.
- Behavior:
- Mark the corresponding `Entry` as `Empty`.
- Merge with adjacent `Empty` entries in both directions (boundary and level constraints).
- Remove merged neighbors from buckets, update position chain, and optionally promote level.
- Re-enqueue the final `Entry` into the appropriate bucket.
### Collect()
- Purpose: Reclaim and rebalance free entries without touching allocated blocks.
- Behavior:
- Consolidate fragmented regions where possible and improve bucket coverage.
- Intended to be cheap; safe to call between benchmark phases.
### BenchmarkDotNet (HLSF vs Libc)
Environment: .NET 8.0.23 (Release), x64 RyuJIT AVX2, Windows 11, AMD Ryzen 7 6800H.
| Method | Mean | StdDev | Ratio | Allocated |
|------- |-----:|------:|------:|----------:|
| Hlsf | 838.4 µs | 40.6 µs | 1.00 | 64 B |
| Libc | 849.8 µs | 100.2 µs | 1.01 | 64 B |
Interpretation: HLSF matches libc allocation throughput under the same workload, with slightly tighter dispersion in this run.
> Note: BenchmarkDotNet also warned that the minimum iteration time was under 1ms; for more stable statistics, increase per-iteration work (e.g., higher `AllocationCount`).
## Detailed Design (summary)
-
- Size classes: The default level size array is [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216] (10 levels, 64B × 4^level). When `maxBlockSize` is specified at construction and exceeds the default range, additional levels are appended (each 4× the previous) to cover the requested maximum block size.
- Segments per level: 1x, 2x, 3x
- Buckets: index = 3 * level + segment (overflow bucket for level > 9)
- Metadata: Entry (external SLAB, position chain), BucketNode (user region, bucket chain)
- Position chain: circular doubly linked list anchored by spare sentinel
- Allocation: search buckets by level/segment with escalation; if none, carve 32MB from spare; set Alloc; slice remainder into buckets
- Slicing: align to level boundaries, pack contiguous blocks forward/backward to minimize overhead
- Free/Merge: bidirectional merge under boundary and level constraints; update buckets; optional level promotion
- Alignment: default 16B; internal blocks aligned by slicing/edge calculation
- Complexity: average O(1) with bounded levels and constant-time bucket ops
- `Entry` (external SLAB): linkage in position chain, `Position`, `Size`, `Level`, `State`
- Invariants: near neighbors must form a consistent double-linked ring; `Level` setter validates size vs. level window
- `BucketNode` (in user region): `EntryRef`, `NextLevel`, `PreviousLevel`, `IsInBucket`
- Position chain: circular doubly linked list anchored by sentinel `_spareMemory` (`State.Spare`)
### Allocation Flow
1) Compute `level` and `segment` from requested `size`
2) Try pop bucket (`TryPopLevelForAllocation(level, segment, out entry)`) and escalate:
- Advance `segment` 0��1��2, then `level++`, until `MaxLevel`
- If still empty: try pop from overflow bucket (L=10, seg=1) or `TryAllocateNew32MBBlock`
3) On success: bound-check the slice offset; set `entry` to `Alloc` using `SetEntryLevel`
4) If remainder exists in the source region, call `SliceMemory(entry->NextNear, remainder)` to produce free chunks into buckets
### Slicing Logic (`SliceMemory`)
- Inputs: `next` entry position, `sizeToSlice`
- Compute `offset` and `end` relative to base buffer; pick starting level by trailing-zero count of `offset`:
- `i = (BitOperations.TrailingZeroCount(offset) - 6) / 2`, clamped to `�� MaxLevel`
- Three phases to cover [offset, end):
1) Forward levels i..N-1: for each level size `size` and `upper=size*4`, align to `edge=((offset+upper-1)/upper)*upper`, pack `count = ((min(edge,end) - offset) / size)` blocks
2) If `i > MaxLevel`: pack in largest upper window on tail
3) Backward i-1..0: fill remaining tail with smaller sizes
- For each packed region: allocate new `Entry`, insert before `next`, mark `Empty`, and push corresponding `BucketNode` into the `(level, segment)` bucket (`PushBucketToLevel(i, count-1, b)`)
### Free and Merge (`MergeAndRemoveFromBuckets`)
- Given an `Entry` to free (turned `Empty` by caller):
- Merge left while neighbor `p` is `Empty`, same `Level`, and within boundary (`AssertBoundary`)
- Remove `p` from bucket (`RemoveFromBucket`), expand current, fix `Position`, unlink `p`
- Merge right with neighbor `n` by the same rule
- Optionally promote `Level` if `Size �� levelSizeArray[originLevel] * 4`
- Ensure `BucketNode.EntryRef` points to the merged entry; caller re-enqueues into the proper bucket
### Buckets
- `PushBucketToLevel(level, segment, node)`: push to bucket head, set `IsInBucket`, maintain prev/next
- `TryPopLevel(level, segment, out node)`: pop head; if empty, return false
- `RemoveFromBucket(level, segment, node)`: unlink from middle or use `TryPopLevel` if it is the head
### Large Allocations and Spare Memory
- Requests exceeding the configured `maxBlockSize` are rejected with `InvalidOperationException`. All allocations are served from the pre-allocated arena.
- When all buckets are empty, `TryAllocateNew32MBBlock` carves a region (up to 32MB) from `_spareMemory` to replenish the bucket system, builds an `Empty` entry, and advances the spare pointer
### Alignment
- Default `_align = 16`; `AlignOfLevel(level)` returns `min(levelSizeArray[level], _align)`
- Internal blocks align to level boundaries by design of slicing/edge calculations
### Complexity and Invariants
- Allocate/free are O(1) on average with bounded levels (��10) and constant-time bucket ops
- Invariants guarded by exceptions (e.g., `HLSFPositionChainBreakException`) and pointer consistency checks
## Profiling (hotspots, current workloads)
- SliceMemory ~22% (self ~17%) �� slicing and bucket enqueue dominate allocation path
- MergeAndRemoveFromBuckets ~13% (self ~12%) �� merge + removal dominate free path
- Collect ~7.6% (self ~7.5%) �� low overall cost
Focus: reduce per-slice math and bucket ops; add fast-paths for aligned regions; batch/lazy removals after merges.
@@ -1,56 +1,56 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SlabAllocationHandle"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Runtime.CompilerServices;
namespace StgSharp.Mathematics.Memory
{
public unsafe struct SlabAllocationHandle<T> : IDisposable where T: unmanaged
{
private readonly SlabAllocator<T> _allocator;
internal SlabAllocationHandle(SlabAllocator<T> allocator, bool locked)
{
_allocator = allocator;
Locked = locked;
}
public bool Locked { get; set; }
public void Dispose()
{
if (Locked)
{
_allocator.ExitBufferReading();
Locked = false;
}
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SlabAllocationHandle"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Runtime.CompilerServices;
namespace StgSharp.Mathematics.Memory
{
public unsafe struct SlabAllocationHandle<T> : IDisposable where T: unmanaged
{
private readonly SlabAllocator<T> _allocator;
internal SlabAllocationHandle(SlabAllocator<T> allocator, bool locked)
{
_allocator = allocator;
Locked = locked;
}
public bool Locked { get; set; }
public void Dispose()
{
if (Locked)
{
_allocator.ExitBufferReading();
Locked = false;
}
}
}
}
@@ -1,134 +1,134 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SlabAllocator.Chunked"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Threading;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
namespace StgSharp.Mathematics.Memory
{
internal sealed unsafe class ChunkedSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
{
private bool _disposed;
private BufferStack<nuint> _buffers;
private BufferStack<nuint> _recycle;
private readonly int _elementSize = Unsafe.SizeOf<T>();
private nuint _currentBuffer;
private nuint _currentCapacity, _currentIndex;
public ChunkedSlabAllocator(nuint count)
{
_currentBuffer = (nuint)NativeMemory.Alloc(count * (nuint)_elementSize);
_currentCapacity = count;
int initCount = count switch
{
<= 4 => 4,
> 64 => 64,
_ => (int)count
};
Span<nuint> span = stackalloc nuint[initCount];
for (int i = 0; i < initCount; i++) {
span[i] = _currentBuffer + (nuint)(i * _elementSize);
}
_buffers = new(4);
_buffers.Push(_currentBuffer);
_recycle = BufferStackBuilder.Create(span);
_currentCapacity = count;
_currentIndex = (nuint)initCount;
}
public override nuint Allocate()
{
if (_recycle.TryPop(out nuint handle)) {
return handle;
}
// nuint bufferHandle = _currentBuffer;
nuint curIndex = _currentIndex;
if (curIndex >= _currentCapacity)
{
nuint cap = _currentCapacity;
nuint newBuffer = (nuint)NativeMemory.Alloc(cap * (nuint)_elementSize);
_buffers.Push(newBuffer);
_currentBuffer = newBuffer;
_currentIndex = 0;
}
curIndex = _currentIndex;
_currentIndex += 1;
return _currentBuffer + ((nuint)_elementSize * curIndex);
}
public override void Dispose()
{
if (_disposed) {
return;
}
_disposed = true;
// NativeMemory.Free((void*)_currentBuffer);
while (_buffers.TryPop(out nuint buffer))
{
NativeMemory.Free((void*)buffer);
}
_buffers.Dispose();
_recycle.Dispose();
GC.SuppressFinalize(this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void EnterBufferReading() { }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void ExitBufferReading() { }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Free(nuint index)
{
_recycle.Push(index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override SlabAllocationHandle<T> ReadAllocation()
{
return new SlabAllocationHandle<T>(this, false);
}
~ChunkedSlabAllocator()
{
Dispose();
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SlabAllocator.Chunked"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Threading;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
namespace StgSharp.Mathematics.Memory
{
internal sealed unsafe class ChunkedSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
{
private bool _disposed;
private BufferStack<nuint> _buffers;
private BufferStack<nuint> _recycle;
private readonly int _elementSize = Unsafe.SizeOf<T>();
private nuint _currentBuffer;
private nuint _currentCapacity, _currentIndex;
public ChunkedSlabAllocator(nuint count)
{
_currentBuffer = (nuint)NativeMemory.Alloc(count * (nuint)_elementSize);
_currentCapacity = count;
int initCount = count switch
{
<= 4 => 4,
> 64 => 64,
_ => (int)count
};
Span<nuint> span = stackalloc nuint[initCount];
for (int i = 0; i < initCount; i++) {
span[i] = _currentBuffer + (nuint)(i * _elementSize);
}
_buffers = new(4);
_buffers.Push(_currentBuffer);
_recycle = BufferStackBuilder.Create(span);
_currentCapacity = count;
_currentIndex = (nuint)initCount;
}
public override nuint Allocate()
{
if (_recycle.TryPop(out nuint handle)) {
return handle;
}
// nuint bufferHandle = _currentBuffer;
nuint curIndex = _currentIndex;
if (curIndex >= _currentCapacity)
{
nuint cap = _currentCapacity;
nuint newBuffer = (nuint)NativeMemory.Alloc(cap * (nuint)_elementSize);
_buffers.Push(newBuffer);
_currentBuffer = newBuffer;
_currentIndex = 0;
}
curIndex = _currentIndex;
_currentIndex += 1;
return _currentBuffer + ((nuint)_elementSize * curIndex);
}
public override void Dispose()
{
if (_disposed) {
return;
}
_disposed = true;
// NativeMemory.Free((void*)_currentBuffer);
while (_buffers.TryPop(out nuint buffer))
{
NativeMemory.Free((void*)buffer);
}
_buffers.Dispose();
_recycle.Dispose();
GC.SuppressFinalize(this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void EnterBufferReading() { }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void ExitBufferReading() { }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Free(nuint index)
{
_recycle.Push(index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override SlabAllocationHandle<T> ReadAllocation()
{
return new SlabAllocationHandle<T>(this, false);
}
~ChunkedSlabAllocator()
{
Dispose();
}
}
}
@@ -1,157 +1,157 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SlabAllocator.ChunkedConcurrent"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Threading;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.Mathematics.Memory
{
internal sealed unsafe class ConcurrentChunkedSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
{
private bool _disposed;
private readonly BufferExpansionLock _lock = new();
private ConcurrentBufferStack<nuint> _buffers;
private ConcurrentBufferStack<nuint> _recycle;
private readonly int _elementSize = Unsafe.SizeOf<T>();
private nuint _currentBuffer;
private nuint _currentCapacity, _currentIndex;
public ConcurrentChunkedSlabAllocator(nuint count)
{
_currentBuffer = (nuint)NativeMemory.Alloc(count * (nuint)_elementSize);
_currentCapacity = count;
int initCount = count switch
{
<= 4 => 4,
> 64 => 64,
_ => (int)count
};
Span<nuint> span = stackalloc nuint[initCount];
for (int i = 0; i < initCount; i++) {
span[i] = _currentBuffer + (nuint)(i * _elementSize);
}
_buffers = new(4);
_recycle = BufferStackBuilder.CreateConcurrent(span);
_currentCapacity = count;
_currentIndex = (nuint)initCount;
}
public override nuint Allocate()
{
if (_recycle.TryPop(out nuint handle)) {
return handle;
}
while (true)
{
nuint bufferHandle = Volatile.Read(ref _currentBuffer);
nuint curIndex = Volatile.Read(ref _currentIndex);
_lock.EnterMetaDataRead();
try
{
if (curIndex >= _currentCapacity)
{
_lock.ExitMetaDataRead();
_lock.EnterExpansionProcess();
try
{
nuint cap = Volatile.Read(ref _currentCapacity);
nuint newBuffer = (nuint)NativeMemory.Alloc(cap * (nuint)_elementSize);
_buffers.Push(bufferHandle);
_ = Interlocked.Exchange(ref _currentBuffer, newBuffer);
_ = Interlocked.Exchange(ref _currentIndex, 0);
}
finally
{
_lock.ExitExpansionProcess();
}
}
curIndex = Volatile.Read(ref _currentIndex);
_ = Interlocked.Add(
ref Unsafe.As<nuint, ulong>(ref _currentIndex), (ulong)_elementSize);
return _currentBuffer + ((nuint)_elementSize * curIndex);
}
finally
{
if (_lock.IsThreadReadingMetaData) {
_lock.ExitMetaDataRead();
}
}
}
}
public override void Dispose()
{
if (_disposed) {
return;
}
_disposed = true;
NativeMemory.Free((void*)_currentBuffer);
while (_buffers.TryPop(out nuint buffer)) {
NativeMemory.Free((void*)buffer);
}
_lock.Dispose();
_buffers.Dispose();
_recycle.Dispose();
GC.SuppressFinalize(this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void EnterBufferReading() { }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void ExitBufferReading() { }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Free(nuint index)
{
_recycle.Push(index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override SlabAllocationHandle<T> ReadAllocation()
{
return new SlabAllocationHandle<T>(this, false);
}
~ConcurrentChunkedSlabAllocator()
{
Dispose();
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SlabAllocator.ChunkedConcurrent"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Threading;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.Mathematics.Memory
{
internal sealed unsafe class ConcurrentChunkedSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
{
private bool _disposed;
private readonly BufferExpansionLock _lock = new();
private ConcurrentBufferStack<nuint> _buffers;
private ConcurrentBufferStack<nuint> _recycle;
private readonly int _elementSize = Unsafe.SizeOf<T>();
private nuint _currentBuffer;
private nuint _currentCapacity, _currentIndex;
public ConcurrentChunkedSlabAllocator(nuint count)
{
_currentBuffer = (nuint)NativeMemory.Alloc(count * (nuint)_elementSize);
_currentCapacity = count;
int initCount = count switch
{
<= 4 => 4,
> 64 => 64,
_ => (int)count
};
Span<nuint> span = stackalloc nuint[initCount];
for (int i = 0; i < initCount; i++) {
span[i] = _currentBuffer + (nuint)(i * _elementSize);
}
_buffers = new(4);
_recycle = BufferStackBuilder.CreateConcurrent(span);
_currentCapacity = count;
_currentIndex = (nuint)initCount;
}
public override nuint Allocate()
{
if (_recycle.TryPop(out nuint handle)) {
return handle;
}
while (true)
{
nuint bufferHandle = Volatile.Read(ref _currentBuffer);
nuint curIndex = Volatile.Read(ref _currentIndex);
_lock.EnterMetaDataRead();
try
{
if (curIndex >= _currentCapacity)
{
_lock.ExitMetaDataRead();
_lock.EnterExpansionProcess();
try
{
nuint cap = Volatile.Read(ref _currentCapacity);
nuint newBuffer = (nuint)NativeMemory.Alloc(cap * (nuint)_elementSize);
_buffers.Push(bufferHandle);
_ = Interlocked.Exchange(ref _currentBuffer, newBuffer);
_ = Interlocked.Exchange(ref _currentIndex, 0);
}
finally
{
_lock.ExitExpansionProcess();
}
}
curIndex = Volatile.Read(ref _currentIndex);
_ = Interlocked.Add(
ref Unsafe.As<nuint, ulong>(ref _currentIndex), (ulong)_elementSize);
return _currentBuffer + ((nuint)_elementSize * curIndex);
}
finally
{
if (_lock.IsThreadReadingMetaData) {
_lock.ExitMetaDataRead();
}
}
}
}
public override void Dispose()
{
if (_disposed) {
return;
}
_disposed = true;
NativeMemory.Free((void*)_currentBuffer);
while (_buffers.TryPop(out nuint buffer)) {
NativeMemory.Free((void*)buffer);
}
_lock.Dispose();
_buffers.Dispose();
_recycle.Dispose();
GC.SuppressFinalize(this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void EnterBufferReading() { }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void ExitBufferReading() { }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Free(nuint index)
{
_recycle.Push(index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override SlabAllocationHandle<T> ReadAllocation()
{
return new SlabAllocationHandle<T>(this, false);
}
~ConcurrentChunkedSlabAllocator()
{
Dispose();
}
}
}
@@ -1,165 +1,165 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SlabAllocator.SequencialConcurrent"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Threading;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
namespace StgSharp.Mathematics.Memory
{
public sealed unsafe class ConcurrentSequentialSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
{
private T* _buffer;
private bool _disposed;
private readonly BufferExpansionLock _lock = new();
private readonly ConcurrentBufferStack<nuint> _stack;
private readonly int _elementSize;
private nuint _highestAllocated;
private ulong _capacity;
public ConcurrentSequentialSlabAllocator(nuint initialCapacity)
{
_elementSize = Unsafe.SizeOf<T>();
_capacity = initialCapacity;
_highestAllocated = 0;
_buffer = (T*)NativeMemory.Alloc((nuint)_capacity * (nuint)_elementSize);
int initCount = _capacity switch
{
<= 4 => 4,
> 64 => 64,
_ => (int)_capacity / 4
};
Span<nuint> span = stackalloc nuint[initCount];
for (int i = 0; i < initCount; i++) {
span[i] = (nuint)i;
}
_stack = BufferStackBuilder.CreateConcurrent(span);
_highestAllocated += (nuint)initCount;
}
internal T* BasePointer
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _buffer;
}
public override nuint Allocate()
{
if (_stack.TryPop(out nuint index)) {
return index;
}
while (true)
{
index = Volatile.Read(ref _highestAllocated);
_lock.EnterMetaDataRead();
try
{
if (index >= _capacity)
{
_lock.ExitMetaDataRead();
_lock.EnterExpansionProcess();
try
{
_capacity = Volatile.Read(ref _capacity) * 2;
nuint newSize = (nuint)_capacity * (nuint)_elementSize;
_lock.EnterBufferCopy();
try
{
_buffer = (T*)NativeMemory.Realloc(_buffer, newSize);
}
finally
{
_lock.ExitBufferCopy();
}
}
finally
{
_lock.ExitExpansionProcess();
}
}
_ = Interlocked.Increment(ref Unsafe.As<nuint, ulong>(ref _highestAllocated));
return index;
}
finally
{
if (_lock.IsThreadReadingMetaData) {
_lock.ExitMetaDataRead();
}
}
}
}
public override void Dispose()
{
if (_disposed) {
return;
}
_disposed = true;
_stack.Dispose();
NativeMemory.Free(_buffer);
_lock.Dispose();
GC.SuppressFinalize(this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void EnterBufferReading()
{
_lock.EnterBufferRead();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void ExitBufferReading()
{
_lock.ExitBufferRead();
}
public override void Free(nuint index)
{
_stack.Push(index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override SlabAllocationHandle<T> ReadAllocation()
{
EnterBufferReading();
return new SlabAllocationHandle<T>(this, true);
}
~ConcurrentSequentialSlabAllocator()
{
Dispose();
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SlabAllocator.SequencialConcurrent"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Threading;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
namespace StgSharp.Mathematics.Memory
{
public sealed unsafe class ConcurrentSequentialSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
{
private T* _buffer;
private bool _disposed;
private readonly BufferExpansionLock _lock = new();
private readonly ConcurrentBufferStack<nuint> _stack;
private readonly int _elementSize;
private nuint _highestAllocated;
private ulong _capacity;
public ConcurrentSequentialSlabAllocator(nuint initialCapacity)
{
_elementSize = Unsafe.SizeOf<T>();
_capacity = initialCapacity;
_highestAllocated = 0;
_buffer = (T*)NativeMemory.Alloc((nuint)_capacity * (nuint)_elementSize);
int initCount = _capacity switch
{
<= 4 => 4,
> 64 => 64,
_ => (int)_capacity / 4
};
Span<nuint> span = stackalloc nuint[initCount];
for (int i = 0; i < initCount; i++) {
span[i] = (nuint)i;
}
_stack = BufferStackBuilder.CreateConcurrent(span);
_highestAllocated += (nuint)initCount;
}
internal T* BasePointer
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _buffer;
}
public override nuint Allocate()
{
if (_stack.TryPop(out nuint index)) {
return index;
}
while (true)
{
index = Volatile.Read(ref _highestAllocated);
_lock.EnterMetaDataRead();
try
{
if (index >= _capacity)
{
_lock.ExitMetaDataRead();
_lock.EnterExpansionProcess();
try
{
_capacity = Volatile.Read(ref _capacity) * 2;
nuint newSize = (nuint)_capacity * (nuint)_elementSize;
_lock.EnterBufferCopy();
try
{
_buffer = (T*)NativeMemory.Realloc(_buffer, newSize);
}
finally
{
_lock.ExitBufferCopy();
}
}
finally
{
_lock.ExitExpansionProcess();
}
}
_ = Interlocked.Increment(ref Unsafe.As<nuint, ulong>(ref _highestAllocated));
return index;
}
finally
{
if (_lock.IsThreadReadingMetaData) {
_lock.ExitMetaDataRead();
}
}
}
}
public override void Dispose()
{
if (_disposed) {
return;
}
_disposed = true;
_stack.Dispose();
NativeMemory.Free(_buffer);
_lock.Dispose();
GC.SuppressFinalize(this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void EnterBufferReading()
{
_lock.EnterBufferRead();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void ExitBufferReading()
{
_lock.ExitBufferRead();
}
public override void Free(nuint index)
{
_stack.Push(index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override SlabAllocationHandle<T> ReadAllocation()
{
EnterBufferReading();
return new SlabAllocationHandle<T>(this, true);
}
~ConcurrentSequentialSlabAllocator()
{
Dispose();
}
}
}
@@ -1,118 +1,118 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SlabAllocator"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
namespace StgSharp.Mathematics.Memory
{
public abstract class SlabAllocator<T> : IDisposable where T: unmanaged
{
public abstract nuint Allocate();
/// <summary>
/// Creates a new SLAB allocator instance with the specified configuration.
/// </summary>
/// <param name="count">
/// Initial number of objects that can be allocated.
/// </param>
/// <param name="layout">
/// Memory layout strategy for the SLAB allocator.
/// </param>
/// <param name="concurrentSupport">
/// Whether to enable concurrent access support.
/// </param>
/// <returns>
/// A new SLAB allocator instance.
/// </returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when an invalid layout is specified.
/// </exception>
/// <exception cref="NotSupportedException">
/// Thrown when requesting unsupported configurations.
/// </exception>
/// <remarks>
/// <para><strong> Important Threading Considerations: </strong></para> <para>While SLAB
/// /// allocators themselves may be designed for single-threaded use, allocated objects
/// are /// frequently shared across multiple threads in real-world applications. Due to
/// the /// special implementation of expandable concurrent sequential SLAB allocators,
/// they rely /// on internal expansion locks to ensure that memory buffer expansions
/// (which involve /// copying the entire buffer to a new location) do not occur while
/// other threads are /// reading from allocated regions. </para> <para><strong>
/// Sequential Layout Limitations: ///</strong><br /> Sequential layout SLAB allocators
/// cannot provide a single-threaded /// version because:<list
/// type="bullet"><item><description>They use contiguous memory /// layout that requires
/// buffer expansion when capacity is exceeded
/// ///</description></item><item><description>Buffer expansion involves copying all ///
/// existing data to a new memory location</description></item><item><description> ///
/// Without proper synchronization, concurrent access to allocated objects during ///
/// expansion would cause memory corruption</description></item><item><description>The
/// /// expansion lock mechanism is essential even if allocation itself is single- ///
/// threaded </description></item></list></para> <para><strong>Chunked Layout:
/// ///</strong><br /> Chunked layout allocators can optionally provide single-threaded
/// /// versions since they don't require buffer expansion and copying. Each chunk is
/// /// allocated independently, avoiding the memory relocation issues present in
/// sequential /// layouts. </para>
/// </remarks>
public static SlabAllocator<T> Create(nuint count, SlabBufferLayout layout, bool concurrentSupport = true)
{
if (concurrentSupport) {
return layout switch
{
SlabBufferLayout.Sequential => new ConcurrentSequentialSlabAllocator<T>(count),
SlabBufferLayout.Chunked => new ConcurrentChunkedSlabAllocator<T>(count),
_ => throw new ArgumentOutOfRangeException(nameof(layout), layout, null)
};
}
return layout switch
{
SlabBufferLayout.Sequential => throw new NotSupportedException("Non-concurrent version of sequential layout SLAB is not supported due to expansion lock requirements for memory safety during buffer expansion."),
SlabBufferLayout.Chunked => new ChunkedSlabAllocator<T>(count),
_ => throw new ArgumentOutOfRangeException(nameof(layout), layout, null)
};
}
public abstract void Dispose();
public abstract void EnterBufferReading();
public abstract void ExitBufferReading();
public abstract void Free(nuint index);
public abstract SlabAllocationHandle<T> ReadAllocation();
}
public enum SlabBufferLayout
{
Sequential,
Chunked
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SlabAllocator"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
namespace StgSharp.Mathematics.Memory
{
public abstract class SlabAllocator<T> : IDisposable where T: unmanaged
{
public abstract nuint Allocate();
/// <summary>
/// Creates a new SLAB allocator instance with the specified configuration.
/// </summary>
/// <param name="count">
/// Initial number of objects that can be allocated.
/// </param>
/// <param name="layout">
/// Memory layout strategy for the SLAB allocator.
/// </param>
/// <param name="concurrentSupport">
/// Whether to enable concurrent access support.
/// </param>
/// <returns>
/// A new SLAB allocator instance.
/// </returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when an invalid layout is specified.
/// </exception>
/// <exception cref="NotSupportedException">
/// Thrown when requesting unsupported configurations.
/// </exception>
/// <remarks>
/// <para><strong> Important Threading Considerations: </strong></para> <para>While SLAB
/// /// allocators themselves may be designed for single-threaded use, allocated objects
/// are /// frequently shared across multiple threads in real-world applications. Due to
/// the /// special implementation of expandable concurrent sequential SLAB allocators,
/// they rely /// on internal expansion locks to ensure that memory buffer expansions
/// (which involve /// copying the entire buffer to a new location) do not occur while
/// other threads are /// reading from allocated regions. </para> <para><strong>
/// Sequential Layout Limitations: ///</strong><br /> Sequential layout SLAB allocators
/// cannot provide a single-threaded /// version because:<list
/// type="bullet"><item><description>They use contiguous memory /// layout that requires
/// buffer expansion when capacity is exceeded
/// ///</description></item><item><description>Buffer expansion involves copying all ///
/// existing data to a new memory location</description></item><item><description> ///
/// Without proper synchronization, concurrent access to allocated objects during ///
/// expansion would cause memory corruption</description></item><item><description>The
/// /// expansion lock mechanism is essential even if allocation itself is single- ///
/// threaded </description></item></list></para> <para><strong>Chunked Layout:
/// ///</strong><br /> Chunked layout allocators can optionally provide single-threaded
/// /// versions since they don't require buffer expansion and copying. Each chunk is
/// /// allocated independently, avoiding the memory relocation issues present in
/// sequential /// layouts. </para>
/// </remarks>
public static SlabAllocator<T> Create(nuint count, SlabBufferLayout layout, bool concurrentSupport = true)
{
if (concurrentSupport) {
return layout switch
{
SlabBufferLayout.Sequential => new ConcurrentSequentialSlabAllocator<T>(count),
SlabBufferLayout.Chunked => new ConcurrentChunkedSlabAllocator<T>(count),
_ => throw new ArgumentOutOfRangeException(nameof(layout), layout, null)
};
}
return layout switch
{
SlabBufferLayout.Sequential => throw new NotSupportedException("Non-concurrent version of sequential layout SLAB is not supported due to expansion lock requirements for memory safety during buffer expansion."),
SlabBufferLayout.Chunked => new ChunkedSlabAllocator<T>(count),
_ => throw new ArgumentOutOfRangeException(nameof(layout), layout, null)
};
}
public abstract void Dispose();
public abstract void EnterBufferReading();
public abstract void ExitBufferReading();
public abstract void Free(nuint index);
public abstract SlabAllocationHandle<T> ReadAllocation();
}
public enum SlabBufferLayout
{
Sequential,
Chunked
}
}
@@ -1,98 +1,98 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="IRegisterType"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Mathematics
{
/// <summary>
/// Abstraction for a wide SIMD register or equivalent packed numeric storage. Provides raw
/// reference access, broadcast initialization and element level access. Typical
/// implementations map to 128/256/512-bit hardware registers or software emulated blocks.
/// </summary>
public interface IRegisterPresentation
{
/// <summary>
/// Gets a reference to the first element of the register reinterpreted as type <typeparamref
/// name="T" />.
/// </summary>
/// <typeparam name="T">
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
/// </typeparam>
/// <returns>
/// Writable reference to element 0 (lowest lane / lowest bits).
/// </returns>
/// <remarks>
/// Intended for treating the register as a contiguous block of elements. The returned reference
/// must remain valid for the lifetime of the register and be identical to <see
/// cref="Member{T}(int)" /> with index 0. Caller must choose a type layout compatible with the
/// actual register representation.
/// </remarks>
ref T AsRef<T>() where T: unmanaged, INumber<T>;
/// <summary>
/// Fills all elements (lanes) of the register with the provided scalar <paramref name="value" />.
/// </summary>
/// <typeparam name="T">
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
/// </typeparam>
/// <param name="value">
/// Scalar value to replicate to every element.
/// </param>
/// <remarks>
/// Should map to a single broadcast/shuffle instruction where possible. After completion all
/// lanes read back the same value.
/// </remarks>
void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>;
/// <summary>
/// Gets a writable reference to the element at the specified <paramref name="index" />.
/// </summary>
/// <typeparam name="T">
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
/// </typeparam>
/// <param name="index">
/// Zero-based lane index (range defined by implementation).
/// </param>
/// <returns>
/// Writable reference to the lane.
/// </returns>
/// <remarks>
/// Enables scalar lane edits without unpacking the entire register. Implementations may omit
/// bounds checks for performance. Caller is responsible for passing a valid index.
/// </remarks>
ref T Member<T>(int index) where T: unmanaged, INumber<T>;
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="IRegisterType"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Mathematics
{
/// <summary>
/// Abstraction for a wide SIMD register or equivalent packed numeric storage. Provides raw
/// reference access, broadcast initialization and element level access. Typical
/// implementations map to 128/256/512-bit hardware registers or software emulated blocks.
/// </summary>
public interface IRegisterPresentation
{
/// <summary>
/// Gets a reference to the first element of the register reinterpreted as type <typeparamref
/// name="T" />.
/// </summary>
/// <typeparam name="T">
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
/// </typeparam>
/// <returns>
/// Writable reference to element 0 (lowest lane / lowest bits).
/// </returns>
/// <remarks>
/// Intended for treating the register as a contiguous block of elements. The returned reference
/// must remain valid for the lifetime of the register and be identical to <see
/// cref="Member{T}(int)" /> with index 0. Caller must choose a type layout compatible with the
/// actual register representation.
/// </remarks>
ref T AsRef<T>() where T: unmanaged, INumber<T>;
/// <summary>
/// Fills all elements (lanes) of the register with the provided scalar <paramref name="value" />.
/// </summary>
/// <typeparam name="T">
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
/// </typeparam>
/// <param name="value">
/// Scalar value to replicate to every element.
/// </param>
/// <remarks>
/// Should map to a single broadcast/shuffle instruction where possible. After completion all
/// lanes read back the same value.
/// </remarks>
void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>;
/// <summary>
/// Gets a writable reference to the element at the specified <paramref name="index" />.
/// </summary>
/// <typeparam name="T">
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
/// </typeparam>
/// <param name="index">
/// Zero-based lane index (range defined by implementation).
/// </param>
/// <returns>
/// Writable reference to the lane.
/// </returns>
/// <remarks>
/// Enables scalar lane edits without unpacking the entire register. Implementations may omit
/// bounds checks for performance. Caller is responsible for passing a valid index.
/// </remarks>
ref T Member<T>(int index) where T: unmanaged, INumber<T>;
}
}
@@ -1,120 +1,120 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="M128"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using System.Text;
namespace StgSharp.Mathematics
{
[StructLayout(LayoutKind.Explicit, Pack = 16)]
public unsafe struct M128 : IRegisterPresentation
{
[FieldOffset(0)] internal fixed byte Buffer[16];
[FieldOffset(0)] internal Vector128<float> Vec;
internal M128(Vector4 v)
{
Unsafe.SkipInit(out this);
Unsafe.SkipInit(out Vec);
Vec = v.AsVector128();
}
internal M128(Vector128<float> v)
{
Unsafe.SkipInit(out this);
Vec = v;
}
public ref T AsRef<T>() where T: unmanaged, INumber<T>
{
return ref Unsafe.As<byte, T>(ref Buffer[0]);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 AsVector4()
{
return Vec.AsVector4();
}
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
{
Vec = Vector128.Create<T>(value).AsSingle();
}
public override readonly bool Equals([NotNullWhen(true)] object obj)
{
return obj is M128 other && this == other;
}
public override readonly int GetHashCode()
{
return Vec.GetHashCode();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
{
return ref Unsafe.Add(ref AsRef<T>(), index);
}
public override string ToString()
{
return string.Empty;
}
public string ToString(int sample)
{
string str = string.Empty;
for (int i = 15; i >= 0; i--) {
str += $"{Convert.ToString(Buffer[i], sample)} ";
}
return str;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(M128 left, M128 right)
{
return left.Vec != right.Vec;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(M128 left, M128 right)
{
return left.Vec == right.Vec;
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="M128"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using System.Text;
namespace StgSharp.Mathematics
{
[StructLayout(LayoutKind.Explicit, Pack = 16)]
public unsafe struct M128 : IRegisterPresentation
{
[FieldOffset(0)] internal fixed byte Buffer[16];
[FieldOffset(0)] internal Vector128<float> Vec;
internal M128(Vector4 v)
{
Unsafe.SkipInit(out this);
Unsafe.SkipInit(out Vec);
Vec = v.AsVector128();
}
internal M128(Vector128<float> v)
{
Unsafe.SkipInit(out this);
Vec = v;
}
public ref T AsRef<T>() where T: unmanaged, INumber<T>
{
return ref Unsafe.As<byte, T>(ref Buffer[0]);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 AsVector4()
{
return Vec.AsVector4();
}
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
{
Vec = Vector128.Create<T>(value).AsSingle();
}
public override readonly bool Equals([NotNullWhen(true)] object obj)
{
return obj is M128 other && this == other;
}
public override readonly int GetHashCode()
{
return Vec.GetHashCode();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
{
return ref Unsafe.Add(ref AsRef<T>(), index);
}
public override string ToString()
{
return string.Empty;
}
public string ToString(int sample)
{
string str = string.Empty;
for (int i = 15; i >= 0; i--) {
str += $"{Convert.ToString(Buffer[i], sample)} ";
}
return str;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(M128 left, M128 right)
{
return left.Vec != right.Vec;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(M128 left, M128 right)
{
return left.Vec == right.Vec;
}
}
}
@@ -1,65 +1,65 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="M256"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Mathematics
{
[StructLayout(LayoutKind.Explicit, Pack = 16)]
public unsafe struct M256 : IRegisterPresentation
{
[FieldOffset(0)] internal fixed byte Buffer[32];
[FieldOffset(0)] internal Vector256<float> Vec;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T AsRef<T>() where T: unmanaged, INumber<T>
{
return ref Unsafe.As<byte, T>(ref Buffer[0]);
}
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
{
Vec = Vector256.Create(value).AsSingle();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
{
return ref Unsafe.Add(ref AsRef<T>(), index);
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="M256"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Mathematics
{
[StructLayout(LayoutKind.Explicit, Pack = 16)]
public unsafe struct M256 : IRegisterPresentation
{
[FieldOffset(0)] internal fixed byte Buffer[32];
[FieldOffset(0)] internal Vector256<float> Vec;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T AsRef<T>() where T: unmanaged, INumber<T>
{
return ref Unsafe.As<byte, T>(ref Buffer[0]);
}
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
{
Vec = Vector256.Create(value).AsSingle();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
{
return ref Unsafe.Add(ref AsRef<T>(), index);
}
}
}
@@ -1,63 +1,63 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="M512"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Mathematics
{
[StructLayout(LayoutKind.Explicit, Size = 64, Pack = 64)]
public unsafe struct M512 : IRegisterPresentation
{
[FieldOffset(0)] private fixed byte _buffer[64];
[FieldOffset(0)] private Vector512<float> Vec;
public ref T AsRef<T>() where T: unmanaged, INumber<T>
{
return ref Unsafe.As<byte, T>(ref _buffer[0]);
}
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
{
Vec = Vector512.Create(value).AsSingle();
}
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
{
return ref Unsafe.As<byte, T>(ref _buffer[0]);
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="M512"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Mathematics
{
[StructLayout(LayoutKind.Explicit, Size = 64, Pack = 64)]
public unsafe struct M512 : IRegisterPresentation
{
[FieldOffset(0)] private fixed byte _buffer[64];
[FieldOffset(0)] private Vector512<float> Vec;
public ref T AsRef<T>() where T: unmanaged, INumber<T>
{
return ref Unsafe.As<byte, T>(ref _buffer[0]);
}
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
{
Vec = Vector512.Create(value).AsSingle();
}
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
{
return ref Unsafe.As<byte, T>(ref _buffer[0]);
}
}
}
@@ -1,100 +1,100 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="M64"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
namespace StgSharp.Mathematics
{
[StructLayout(LayoutKind.Explicit, Pack = 8)]
public unsafe struct M64 : IRegisterPresentation
{
[FieldOffset(0)] private fixed byte buffer[8];
[FieldOffset(0)] private ulong value;
[FieldOffset(0)] private Vector64<float> vec;
public M64()
{
Unsafe.SkipInit(out this);
Unsafe.SkipInit(out vec);
Unsafe.SkipInit(out value);
}
public ref T AsRef<T>() where T: unmanaged,INumber<T>
{
return ref Unsafe.As<byte, T>(ref buffer[0]);
}
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
{
vec = Vector64.Create(value).AsSingle();
}
public override bool Equals(object obj)
{
return obj is M64 m64 && this == m64;
}
public override readonly int GetHashCode()
{
return value.GetHashCode();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
{
return ref Unsafe.As<byte, T>(ref buffer[index * sizeof(T)]);
}
public static bool operator !=(M64 left, M64 right)
{
return !(left == right);
}
public static M64 operator <<(M64 value, int shift)
{
M64 result = new M64();
result.value = value.value << shift;
return result;
}
public static bool operator ==(M64 left, M64 right)
{
return left.value == right.value;
}
public static M64 operator >>(M64 value, int shift)
{
M64 result = new M64();
result.value = value.value << shift;
return result;
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="M64"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
namespace StgSharp.Mathematics
{
[StructLayout(LayoutKind.Explicit, Pack = 8)]
public unsafe struct M64 : IRegisterPresentation
{
[FieldOffset(0)] private fixed byte buffer[8];
[FieldOffset(0)] private ulong value;
[FieldOffset(0)] private Vector64<float> vec;
public M64()
{
Unsafe.SkipInit(out this);
Unsafe.SkipInit(out vec);
Unsafe.SkipInit(out value);
}
public ref T AsRef<T>() where T: unmanaged,INumber<T>
{
return ref Unsafe.As<byte, T>(ref buffer[0]);
}
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
{
vec = Vector64.Create(value).AsSingle();
}
public override bool Equals(object obj)
{
return obj is M64 m64 && this == m64;
}
public override readonly int GetHashCode()
{
return value.GetHashCode();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
{
return ref Unsafe.As<byte, T>(ref buffer[index * sizeof(T)]);
}
public static bool operator !=(M64 left, M64 right)
{
return !(left == right);
}
public static M64 operator <<(M64 value, int shift)
{
M64 result = new M64();
result.value = value.value << shift;
return result;
}
public static bool operator ==(M64 left, M64 right)
{
return left.value == right.value;
}
public static M64 operator >>(M64 value, int shift)
{
M64 result = new M64();
result.value = value.value << shift;
return result;
}
}
}
+133 -133
View File
@@ -1,133 +1,133 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="DefaultLog"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
namespace StgSharp.Internal
{
internal static unsafe partial class DefaultLog
{
private const long MaxLogFileSize = 10 * 1024 * 1024; // 10 MB
private static LogType _currentLogLevel = LogType.Info;
internal static readonly GCHandle ssc_logHandle =
GCHandle.Alloc(new byte[512], GCHandleType.Pinned);
internal static SemaphoreSlim logSyncSemaphore = new(1, 1);
internal static void InternalAppendLog(
string log
)
{
if (!File.Exists(Native.LogPath))
{
FileStream stream = File.Create(Native.LogPath);
stream.Close();
}
using (StreamWriter logStream = File.AppendText(Native.LogPath)) {
logStream.WriteLine(log);
}
}
internal static void InternalWriteLog(
string logLine,
LogType logType
)
{
if (logType < _currentLogLevel) {
return;
}
logSyncSemaphore.Wait();
try
{
RotateLogFile();
if (!File.Exists(Native.LogPath))
{
FileStream stream = File.Create(Native.LogPath);
stream.Close();
}
using (StreamWriter logStream = File.AppendText(Native.LogPath)) {
logStream.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] [{logType}] {logLine}");
}
}
finally
{
_ = logSyncSemaphore.Release();
}
}
internal static void InternalWriteLog(
string beforeTime,
string logLine,
LogType logType
)
{
logSyncSemaphore.Wait();
if (!File.Exists(Native.LogPath))
{
FileStream stream = File.Create(Native.LogPath);
stream.Close();
}
using (StreamWriter logStream = File.AppendText(Native.LogPath))
{
logStream.WriteLine(beforeTime);
logStream.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] [{logType}] {logLine}");
}
_ = logSyncSemaphore.Release(1);
}
internal static void SetLogLevel(
LogType logLevel
)
{
_currentLogLevel = logLevel;
}
private static void RotateLogFile()
{
if (File.Exists(Native.LogPath) && new FileInfo(Native.LogPath).Length > MaxLogFileSize)
{
string backupFile = Native.LogPath + ".backup";
if (File.Exists(backupFile)) {
File.Delete(backupFile);
}
File.Move(Native.LogPath, backupFile);
}
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="DefaultLog"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
namespace StgSharp.Internal
{
internal static unsafe partial class DefaultLog
{
private const long MaxLogFileSize = 10 * 1024 * 1024; // 10 MB
private static LogType _currentLogLevel = LogType.Info;
internal static readonly GCHandle ssc_logHandle =
GCHandle.Alloc(new byte[512], GCHandleType.Pinned);
internal static SemaphoreSlim logSyncSemaphore = new(1, 1);
internal static void InternalAppendLog(
string log
)
{
if (!File.Exists(Native.LogPath))
{
FileStream stream = File.Create(Native.LogPath);
stream.Close();
}
using (StreamWriter logStream = File.AppendText(Native.LogPath)) {
logStream.WriteLine(log);
}
}
internal static void InternalWriteLog(
string logLine,
LogType logType
)
{
if (logType < _currentLogLevel) {
return;
}
logSyncSemaphore.Wait();
try
{
RotateLogFile();
if (!File.Exists(Native.LogPath))
{
FileStream stream = File.Create(Native.LogPath);
stream.Close();
}
using (StreamWriter logStream = File.AppendText(Native.LogPath)) {
logStream.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] [{logType}] {logLine}");
}
}
finally
{
_ = logSyncSemaphore.Release();
}
}
internal static void InternalWriteLog(
string beforeTime,
string logLine,
LogType logType
)
{
logSyncSemaphore.Wait();
if (!File.Exists(Native.LogPath))
{
FileStream stream = File.Create(Native.LogPath);
stream.Close();
}
using (StreamWriter logStream = File.AppendText(Native.LogPath))
{
logStream.WriteLine(beforeTime);
logStream.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] [{logType}] {logLine}");
}
_ = logSyncSemaphore.Release(1);
}
internal static void SetLogLevel(
LogType logLevel
)
{
_currentLogLevel = logLevel;
}
private static void RotateLogFile()
{
if (File.Exists(Native.LogPath) && new FileInfo(Native.LogPath).Length > MaxLogFileSize)
{
string backupFile = Native.LogPath + ".backup";
if (File.Exists(backupFile)) {
File.Delete(backupFile);
}
File.Move(Native.LogPath, backupFile);
}
}
}
}
+48 -48
View File
@@ -1,48 +1,48 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="Native"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Internal
{
internal static unsafe partial class Native
{
internal const string LibName =
"StgSharp.Native";
internal const string LogPath =
"SS_error.log";
internal static IntPtr _libPtr = IntPtr.Zero;
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="Native"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Internal
{
internal static unsafe partial class Native
{
internal const string LibName =
"StgSharp.Native";
internal const string LogPath =
"SS_error.log";
internal static IntPtr _libPtr = IntPtr.Zero;
}
}
+45 -45
View File
@@ -1,46 +1,46 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="public"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp
{
public enum LogType
{
Error,
Warning,
Info,
InfoError,
InfoWarning,
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="public"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp
{
public enum LogType
{
Error,
Warning,
Info,
InfoError,
InfoWarning,
}
}
@@ -1,91 +1,91 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="CrossFramePipelineNode"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.PipeLine;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.PipeLine
{
public class CrossFrameOperation : IConvertableToPipelineNode
{
private static string[] _inputName = [ "TaskIn" ];
private static string[] _outputName = [ "TaskOut" ];
private Action _mainExecution;
private int currentCount;
private readonly int cycleCount;
public CrossFrameOperation(Action startup, int cycleCount)
{
MainExecution = startup;
this.cycleCount = (cycleCount > 1) ?
cycleCount :
throw new ArgumentException(
$"{$"The {typeof(CrossFrameOperation).Name} crosses only one cycle, "}{$"please use a {typeof(PipelineNode).Name} instead."}");
this.cycleCount = cycleCount;
currentCount = cycleCount;
}
public Action MainExecution
{
get => _mainExecution;
private protected set => _mainExecution = value;
}
public IEnumerable<string> InputPortName => _inputName;
public IEnumerable<string> OutputPortName => _outputName;
public PipelineNodeOperation Operation
{
get => NodeMain;
}
public void NodeMain(
in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output)
{
if (currentCount < cycleCount)
{
Interlocked.Increment(ref currentCount);
} else
{
Interlocked.Exchange(ref currentCount, 0);
Task.Run(MainExecution);
}
// Console.WriteLine(currentCount);
PipelineNodeOutPort.SkipAll(output);
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="CrossFramePipelineNode"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.PipeLine;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.PipeLine
{
public class CrossFrameOperation : IConvertableToPipelineNode
{
private static string[] _inputName = [ "TaskIn" ];
private static string[] _outputName = [ "TaskOut" ];
private Action _mainExecution;
private int currentCount;
private readonly int cycleCount;
public CrossFrameOperation(Action startup, int cycleCount)
{
MainExecution = startup;
this.cycleCount = (cycleCount > 1) ?
cycleCount :
throw new ArgumentException(
$"{$"The {typeof(CrossFrameOperation).Name} crosses only one cycle, "}{$"please use a {typeof(PipelineNode).Name} instead."}");
this.cycleCount = cycleCount;
currentCount = cycleCount;
}
public Action MainExecution
{
get => _mainExecution;
private protected set => _mainExecution = value;
}
public IEnumerable<string> InputPortName => _inputName;
public IEnumerable<string> OutputPortName => _outputName;
public PipelineNodeOperation Operation
{
get => NodeMain;
}
public void NodeMain(
in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output)
{
if (currentCount < cycleCount)
{
Interlocked.Increment(ref currentCount);
} else
{
Interlocked.Exchange(ref currentCount, 0);
Task.Run(MainExecution);
}
// Console.WriteLine(currentCount);
PipelineNodeOutPort.SkipAll(output);
}
}
}
@@ -1,82 +1,82 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="IConvertableToPipelineNode"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
namespace StgSharp.PipeLine
{
public delegate void PipelineNodeOperation(
in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output);
public interface IConvertableToPipelineNode
{
public IEnumerable<string> InputPortName { get; }
public IEnumerable<string> OutputPortName { get; }
PipelineNodeOperation Operation { get; }
void NodeMain(
in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output);
}
internal class DefaultConvertableToBlueprintNode : IConvertableToPipelineNode
{
private string[] _input;
private string[] _output;
private PipelineNodeOperation _execution;
public DefaultConvertableToBlueprintNode(PipelineNodeOperation execution, string[] input, string[] output)
{
_input = input;
_output = output;
_execution = execution;
}
public IEnumerable<string> InputPortName => _input;
public IEnumerable<string> OutputPortName => _output;
public PipelineNodeOperation Operation => _execution;
public void NodeMain(
in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output)
{
_execution.Invoke(input, output);
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="IConvertableToPipelineNode"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
namespace StgSharp.PipeLine
{
public delegate void PipelineNodeOperation(
in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output);
public interface IConvertableToPipelineNode
{
public IEnumerable<string> InputPortName { get; }
public IEnumerable<string> OutputPortName { get; }
PipelineNodeOperation Operation { get; }
void NodeMain(
in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output);
}
internal class DefaultConvertableToBlueprintNode : IConvertableToPipelineNode
{
private string[] _input;
private string[] _output;
private PipelineNodeOperation _execution;
public DefaultConvertableToBlueprintNode(PipelineNodeOperation execution, string[] input, string[] output)
{
_input = input;
_output = output;
_execution = execution;
}
public IEnumerable<string> InputPortName => _input;
public IEnumerable<string> OutputPortName => _output;
public PipelineNodeOperation Operation => _execution;
public void NodeMain(
in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output)
{
_execution.Invoke(input, output);
}
}
}
+242 -242
View File
@@ -1,242 +1,242 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="PipeLineRunner"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Threading;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.PipeLine
{
public static class PipeLineRunner
{
private static NodeRunner[] allAvailableTask = CreateBlueprintRunnerArray();
private static CancellationTokenSource cts = new CancellationTokenSource();
private static ConcurrentStack<PipelineScheduler> pipelineInWaiting
= new ConcurrentStack<PipelineScheduler>();
private static int _callingThread;
private static SemaphoreSlim pipelineLock = new SemaphoreSlim(0, 1);
public static void Run(PipelineScheduler pipeline)
{
// Console.WriteLine();
if (pipeline == null)
{
return;
}
if (pipeline.IsRunning) {
throw new InvalidOperationException("Attempt to run a blueprint instance twice.");
}
if (!IsValidCalling()) {
pipelineLock.Wait();
}
if (InterruptAndRunNewPipeline(pipeline))
{
while (InterruptAndRunNewPipeline(NodeRunner.CurrentPipeline!)) { }
}
pipelineLock.Release();
Interlocked.Exchange(ref _callingThread, -1);
}
public static void SetMaxTaskCount(int size)
{
if (size < 1) {
size = 1;
}
if (allAvailableTask.Length >= size)
{
for (int i = size; i < allAvailableTask.Length; i++)
{
if (allAvailableTask[i] != null) {
allAvailableTask[i].Cancel();
}
}
}
Array.Resize(ref allAvailableTask, size);
}
public static void Terminate()
{
cts.Cancel();
if (NodeRunner.CurrentPipeline is null) {
return;
}
NodeRunner.CurrentPipeline.Terminate();
foreach (NodeRunner task in allAvailableTask) {
task.Cts.Cancel();
}
foreach (PipelineScheduler bp in pipelineInWaiting) {
bp.Terminate();
}
pipelineInWaiting.Clear();
}
internal static bool InterruptAndRunNewPipeline(PipelineScheduler newPipeline)
{
ArgumentNullException.ThrowIfNull(newPipeline);
if ((newPipeline != NodeRunner.CurrentPipeline) && (NodeRunner.CurrentPipeline != null))
{
// if a running bp exists
pipelineInWaiting.Push(NodeRunner.CurrentPipeline);
}
// load new bp to current bp
NodeRunner.CurrentPipeline = newPipeline;
Interlocked.Exchange(ref _callingThread, Environment.CurrentManagedThreadId);
// run beginning node to init it first.
if (!NodeRunner.CurrentPipeline.IsRunning)
{
(NodeRunner.CurrentPipeline.BeginLayer as BeginningNode)!.Run();
}
// refresh all tasks
/*
for( int i = 0; i < allAvailableTask.Length; i++ )
{
NodeRunner item = allAvailableTask[ i ];
if( ( item is null ) || item.IsCompleted ) {
allAvailableTask[ i ] = NodeRunner.Run();
}
}
/**/
PipelineNode node;
while (!cts.Token.IsCancellationRequested && NodeRunner.CurrentPipeline.RequestNextMainThreadNode(out node))
{
node.Run();
if (!NodeRunner.CurrentPipeline.IsRunning)
{
break;
}
}
while ((NodeRunner.CurrentPipeline != null) &&
NodeRunner.CurrentPipeline.IsRunning &&
!cts.Token.IsCancellationRequested &&
NodeRunner.CurrentPipeline!.RequestNexConcurrentNode(out node))
{
node.Run();
if (!NodeRunner.CurrentPipeline!.IsRunning)
{
break;
}
}
(NodeRunner.CurrentPipeline!.EndLayer as EndingNode)!.Run();
bool isEmpty = pipelineInWaiting.TryPop(out PipelineScheduler? pipeline);
NodeRunner.CurrentPipeline = pipeline!;
return isEmpty;
}
/// <summary>
/// <para> Only the following occasions of calling are valid: </para> <para> 1. no bp is
/// running or the only bp is quitting. </para><para> 2. calling from the main thread or a
/// thread the current bp is running on.</para>
/// </summary>
internal static bool IsValidCalling()
{
if ((NodeRunner.CurrentPipeline == null) || //no bp running at all
((pipelineInWaiting.IsEmpty) && (NodeRunner.CurrentPipeline.IsRunning == false)))
{
// a bp is quitting
// in case no bp is running
return true;
}
int? id = Task.CurrentId;
if (id is null)
{
// in case calling from main thread and current running bp
return _callingThread == Environment.CurrentManagedThreadId;
}
foreach (NodeRunner item in allAvailableTask)
{
if (item.Id == id)
{
// if calling from any bp running task
return true;
}
}
return false;
}
private static NodeRunner[] CreateBlueprintRunnerArray()
{
int count = Environment.ProcessorCount;
return new NodeRunner[ count < 2 ? count : count - 2];
}
}
internal class NodeRunner : CancellableTask
{
private static volatile PipelineScheduler? _current;
internal NodeRunner()
: base()
{
_startup = InternalRun;
}
internal static PipelineScheduler? CurrentPipeline
{
get => _current;
set => _current = value;
}
internal static NodeRunner Run()
{
NodeRunner ret = new NodeRunner
{
_startup = InternalRun
};
ret._internalTask = Task.Run(ret.InternalAction);
return ret;
}
private static void InternalRun(CancellationToken token)
{
PipelineNode item;
Console.WriteLine($"Global thread {Environment.CurrentManagedThreadId} starts.");
while ((CurrentPipeline != null) &&
CurrentPipeline.IsRunning &&
!token.IsCancellationRequested &&
CurrentPipeline.RequestNexConcurrentNode(out item)) {
item.Run();
}
}
}//------------------------------------- End of Class ------------------------------------------
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="PipeLineRunner"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Threading;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.PipeLine
{
public static class PipeLineRunner
{
private static NodeRunner[] allAvailableTask = CreateBlueprintRunnerArray();
private static CancellationTokenSource cts = new CancellationTokenSource();
private static ConcurrentStack<PipelineScheduler> pipelineInWaiting
= new ConcurrentStack<PipelineScheduler>();
private static int _callingThread;
private static SemaphoreSlim pipelineLock = new SemaphoreSlim(0, 1);
public static void Run(PipelineScheduler pipeline)
{
// Console.WriteLine();
if (pipeline == null)
{
return;
}
if (pipeline.IsRunning) {
throw new InvalidOperationException("Attempt to run a blueprint instance twice.");
}
if (!IsValidCalling()) {
pipelineLock.Wait();
}
if (InterruptAndRunNewPipeline(pipeline))
{
while (InterruptAndRunNewPipeline(NodeRunner.CurrentPipeline!)) { }
}
pipelineLock.Release();
Interlocked.Exchange(ref _callingThread, -1);
}
public static void SetMaxTaskCount(int size)
{
if (size < 1) {
size = 1;
}
if (allAvailableTask.Length >= size)
{
for (int i = size; i < allAvailableTask.Length; i++)
{
if (allAvailableTask[i] != null) {
allAvailableTask[i].Cancel();
}
}
}
Array.Resize(ref allAvailableTask, size);
}
public static void Terminate()
{
cts.Cancel();
if (NodeRunner.CurrentPipeline is null) {
return;
}
NodeRunner.CurrentPipeline.Terminate();
foreach (NodeRunner task in allAvailableTask) {
task.Cts.Cancel();
}
foreach (PipelineScheduler bp in pipelineInWaiting) {
bp.Terminate();
}
pipelineInWaiting.Clear();
}
internal static bool InterruptAndRunNewPipeline(PipelineScheduler newPipeline)
{
ArgumentNullException.ThrowIfNull(newPipeline);
if ((newPipeline != NodeRunner.CurrentPipeline) && (NodeRunner.CurrentPipeline != null))
{
// if a running bp exists
pipelineInWaiting.Push(NodeRunner.CurrentPipeline);
}
// load new bp to current bp
NodeRunner.CurrentPipeline = newPipeline;
Interlocked.Exchange(ref _callingThread, Environment.CurrentManagedThreadId);
// run beginning node to init it first.
if (!NodeRunner.CurrentPipeline.IsRunning)
{
(NodeRunner.CurrentPipeline.BeginLayer as BeginningNode)!.Run();
}
// refresh all tasks
/*
for( int i = 0; i < allAvailableTask.Length; i++ )
{
NodeRunner item = allAvailableTask[ i ];
if( ( item is null ) || item.IsCompleted ) {
allAvailableTask[ i ] = NodeRunner.Run();
}
}
/**/
PipelineNode node;
while (!cts.Token.IsCancellationRequested && NodeRunner.CurrentPipeline.RequestNextMainThreadNode(out node))
{
node.Run();
if (!NodeRunner.CurrentPipeline.IsRunning)
{
break;
}
}
while ((NodeRunner.CurrentPipeline != null) &&
NodeRunner.CurrentPipeline.IsRunning &&
!cts.Token.IsCancellationRequested &&
NodeRunner.CurrentPipeline!.RequestNexConcurrentNode(out node))
{
node.Run();
if (!NodeRunner.CurrentPipeline!.IsRunning)
{
break;
}
}
(NodeRunner.CurrentPipeline!.EndLayer as EndingNode)!.Run();
bool isEmpty = pipelineInWaiting.TryPop(out PipelineScheduler? pipeline);
NodeRunner.CurrentPipeline = pipeline!;
return isEmpty;
}
/// <summary>
/// <para> Only the following occasions of calling are valid: </para> <para> 1. no bp is
/// running or the only bp is quitting. </para><para> 2. calling from the main thread or a
/// thread the current bp is running on.</para>
/// </summary>
internal static bool IsValidCalling()
{
if ((NodeRunner.CurrentPipeline == null) || //no bp running at all
((pipelineInWaiting.IsEmpty) && (NodeRunner.CurrentPipeline.IsRunning == false)))
{
// a bp is quitting
// in case no bp is running
return true;
}
int? id = Task.CurrentId;
if (id is null)
{
// in case calling from main thread and current running bp
return _callingThread == Environment.CurrentManagedThreadId;
}
foreach (NodeRunner item in allAvailableTask)
{
if (item.Id == id)
{
// if calling from any bp running task
return true;
}
}
return false;
}
private static NodeRunner[] CreateBlueprintRunnerArray()
{
int count = Environment.ProcessorCount;
return new NodeRunner[ count < 2 ? count : count - 2];
}
}
internal class NodeRunner : CancellableTask
{
private static volatile PipelineScheduler? _current;
internal NodeRunner()
: base()
{
_startup = InternalRun;
}
internal static PipelineScheduler? CurrentPipeline
{
get => _current;
set => _current = value;
}
internal static NodeRunner Run()
{
NodeRunner ret = new NodeRunner
{
_startup = InternalRun
};
ret._internalTask = Task.Run(ret.InternalAction);
return ret;
}
private static void InternalRun(CancellationToken token)
{
PipelineNode item;
Console.WriteLine($"Global thread {Environment.CurrentManagedThreadId} starts.");
while ((CurrentPipeline != null) &&
CurrentPipeline.IsRunning &&
!token.IsCancellationRequested &&
CurrentPipeline.RequestNexConcurrentNode(out item)) {
item.Run();
}
}
}//------------------------------------- End of Class ------------------------------------------
}
+114 -114
View File
@@ -1,114 +1,114 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="PipelineMarshal"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.PipeLine
{
public static class PipelineMarshal
{
public static IEnumerable<PipelineNode> GetNodeSortedEnumeration(PipelineScheduler scheduler)
{
ArgumentNullException.ThrowIfNull(scheduler);
return new PipelineNodeEnumerator(scheduler)
{
_count = scheduler._allNode.Count,
};
}
private class PipelineNodeEnumerator : ICollection<PipelineNode>
{
private readonly List<List<PipelineNode>> _mt, _c;
public int _count;
public PipelineNodeEnumerator(PipelineScheduler scheduler)
{
if (!scheduler.TryGetAllNode(out _mt, out _c)) {
throw new InvalidOperationException("Binded scheduler is not sorted.");
}
}
public bool IsReadOnly => true;
public int Count => _count;
public IEnumerator<PipelineNode> GetEnumerator()
{
for (int i = 1; i < _mt.Count; i++)
{
foreach (PipelineNode node in _mt[i]) {
yield return node;
}
foreach (PipelineNode node in _c[i]) {
yield return node;
}
}
}
void ICollection<PipelineNode>.Add(PipelineNode item)
{
throw new NotSupportedException();
}
void ICollection<PipelineNode>.Clear()
{
throw new NotSupportedException();
}
bool ICollection<PipelineNode>.Contains(PipelineNode item)
{
throw new NotSupportedException();
}
void ICollection<PipelineNode>.CopyTo(PipelineNode[] array, int arrayIndex)
{
throw new NotSupportedException();
}
bool ICollection<PipelineNode>.Remove(PipelineNode item)
{
throw new NotSupportedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="PipelineMarshal"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.PipeLine
{
public static class PipelineMarshal
{
public static IEnumerable<PipelineNode> GetNodeSortedEnumeration(PipelineScheduler scheduler)
{
ArgumentNullException.ThrowIfNull(scheduler);
return new PipelineNodeEnumerator(scheduler)
{
_count = scheduler._allNode.Count,
};
}
private class PipelineNodeEnumerator : ICollection<PipelineNode>
{
private readonly List<List<PipelineNode>> _mt, _c;
public int _count;
public PipelineNodeEnumerator(PipelineScheduler scheduler)
{
if (!scheduler.TryGetAllNode(out _mt, out _c)) {
throw new InvalidOperationException("Binded scheduler is not sorted.");
}
}
public bool IsReadOnly => true;
public int Count => _count;
public IEnumerator<PipelineNode> GetEnumerator()
{
for (int i = 1; i < _mt.Count; i++)
{
foreach (PipelineNode node in _mt[i]) {
yield return node;
}
foreach (PipelineNode node in _c[i]) {
yield return node;
}
}
}
void ICollection<PipelineNode>.Add(PipelineNode item)
{
throw new NotSupportedException();
}
void ICollection<PipelineNode>.Clear()
{
throw new NotSupportedException();
}
bool ICollection<PipelineNode>.Contains(PipelineNode item)
{
throw new NotSupportedException();
}
void ICollection<PipelineNode>.CopyTo(PipelineNode[] array, int arrayIndex)
{
throw new NotSupportedException();
}
bool ICollection<PipelineNode>.Remove(PipelineNode item)
{
throw new NotSupportedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
}
+266 -266
View File
@@ -1,266 +1,266 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="PipelineNode"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.PipeLine
{
/// <summary>
///
/// </summary>
/// <risk>
/// Reset pipeline has risk, this operation is a direct operation to port in current node, the
/// port on the other node cannot get the change.
/// </risk>
#pragma warning disable CA1036
public class PipelineNode
#pragma warning restore CA1036
{
private static readonly Dictionary<string, PipelineConnector> _emptyLayer = [];
private static PipelineNode _emptyNode = new PipelineNode(
PipelineNodeLabel.Empty, null!, false);
private IConvertableToPipelineNode _mainOperation;
private PipelineNodeLabel _label;
private protected bool _nativeRequested;
private protected Dictionary<string, PipelineNodeInPort> _input;
private protected Dictionary<string, PipelineNodeOutPort> _output;
private protected int _level, _count;
internal PipelineNode(PipelineNodeLabel label, IConvertableToPipelineNode node, bool isNative)
{
_label = label;
_nativeRequested = isNative;
_mainOperation = node;
InputPorts = new();
OutputPorts = new();
foreach (string port in node.InputPortName) {
InputPorts.Add(port, new PipelineNodeInPort(this, port));
}
foreach (string port in node.OutputPortName) {
OutputPorts.Add(port, new PipelineNodeOutPort(this, port));
}
}
#pragma warning disable CS8618
internal PipelineNode(
PipelineNodeOperation operation,
PipelineNodeLabel name,
bool isNative,
string[] inputNames,
string[] outputNames)
#pragma warning restore CS8618
{
if ((inputNames == null) || (inputNames.Length == 0)) {
throw new ArgumentNullException(nameof(inputNames));
}
if ((outputNames == null) || (outputNames.Length == 0)) {
throw new ArgumentNullException(nameof(outputNames));
}
_label = name;
_nativeRequested = isNative;
if (operation == null) {
operation = DefaultOperation;
}
_mainOperation = new DefaultConvertableToBlueprintNode(
operation, inputNames, outputNames);
InputPorts = new Dictionary<string, PipelineNodeInPort>();
OutputPorts = new Dictionary<string, PipelineNodeOutPort>();
foreach (string port in inputNames) {
InputPorts.Add(port, new PipelineNodeInPort(this, port));
}
foreach (string port in outputNames) {
OutputPorts.Add(port, new PipelineNodeOutPort(this, port));
}
}
public bool IsNative
{
get => _nativeRequested;
internal set => _nativeRequested = value;
}
public static Dictionary<string, PipelineConnector> EmptyPortLayer => _emptyLayer;
public IConvertableToPipelineNode NodeOperation => _mainOperation;
public int Level
{
get => _level;
private set => _level = value;
}
public static PipelineNode Empty => _emptyNode;
public PipelineNodeLabel Label => _label;
internal Dictionary<string, PipelineNodeInPort> InputPorts
{
get => _input;
private set => _input = value;
}
internal Dictionary<string, PipelineNodeOutPort> OutputPorts
{
get => _output;
private set => _output = value;
}
internal HashSet<PipelineNode> Previous { get; } = [];
internal HashSet<PipelineNode> Next { get; } = [];
internal IConvertableToPipelineNode Value => _mainOperation;
internal int RemainingIncreaseCount => Previous.Count - _count;
public static void Connect(PipelineNode front, string frontNodePort, string backNodePort, PipelineNode back)
{
ArgumentNullException.ThrowIfNull(front);
ArgumentNullException.ThrowIfNull(back);
if (!front.OutputPorts
.TryGetValue(frontNodePort,
out PipelineNodeOutPort? frontPort)) {
throw new ArgumentOutOfRangeException($"Cannot find port named {frontNodePort}");
}
if (!back.InputPorts.TryGetValue(backNodePort, out PipelineNodeInPort? backPort)) {
throw new ArgumentOutOfRangeException($"Cannot find port named {backNodePort}");
}
backPort.Connect(frontPort);
frontPort.Connect(backPort);
}
public IEnumerator<PipelineNode> GetFormerNodes()
{
foreach (PipelineNodeInPort port in InputPorts.Values)
{
IEnumerator<PipelineNode> enumerator = port.GetFormerNodes();
while (enumerator.MoveNext()) {
yield return enumerator.Current;
}
}
}
public virtual void Run()
{
try
{
// Console.WriteLine(_label);
PipelineNodeInPort.WaitAll(InputPorts);
if (_mainOperation != null) {
_mainOperation.NodeMain(in _input, in _output);
}
}
catch (Exception)
{
throw;
}
}
internal bool TryIncreaseLevelTo(int level)
{
_count++;
if (_count > Previous.Count) {
return false;
}
Level = int.Max(level, this.Level);
return true;
}
protected static void DefaultOperation(
in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output)
{
PipelineNodeOutPort.SkipAll(output);
}
#region port operation
public PipelineNodeInPort GetInputPort(string lable)
{
if (!InputPorts.TryGetValue(lable, out PipelineNodeInPort? ret)) {
throw new ArgumentOutOfRangeException(
$"Cannot find the interface named {lable} in node {Label}.");
}
return ret;
}
public PipelineNodeOutPort GetOutputPort(string lable)
{
if (!OutputPorts.TryGetValue(lable, out PipelineNodeOutPort? ret)) {
throw new ArgumentOutOfRangeException(
$"Cannot find the interface named {lable} in node {Label}.");
}
return ret;
}
public PipelineNodeInPort? DefineCertainInputPort(string label)
{
if (InputPorts.ContainsKey(label)) {
return null;
}
PipelineNodeInPort port = new(this, label);
InputPorts[label] = port;
return port;
}
public PipelineNodeOutPort? DefineCertainOutputPort(string label)
{
if (OutputPorts.ContainsKey(label)) {
return null;
}
PipelineNodeOutPort port = new(this, label);
OutputPorts[label] = port;
return port;
}
#endregion
}//--------------------------------- end of class -----------------------------------
public class PipelineNodeRunException : Exception
{
public PipelineNodeRunException() { }
public PipelineNodeRunException(Exception ex) : base(string.Empty, ex) { }
public PipelineNodeRunException(string message) : base(message) { }
public PipelineNodeRunException(string message, Exception innerException) : base(message, innerException) { }
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="PipelineNode"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.PipeLine
{
/// <summary>
///
/// </summary>
/// <risk>
/// Reset pipeline has risk, this operation is a direct operation to port in current node, the
/// port on the other node cannot get the change.
/// </risk>
#pragma warning disable CA1036
public class PipelineNode
#pragma warning restore CA1036
{
private static readonly Dictionary<string, PipelineConnector> _emptyLayer = [];
private static PipelineNode _emptyNode = new PipelineNode(
PipelineNodeLabel.Empty, null!, false);
private IConvertableToPipelineNode _mainOperation;
private PipelineNodeLabel _label;
private protected bool _nativeRequested;
private protected Dictionary<string, PipelineNodeInPort> _input;
private protected Dictionary<string, PipelineNodeOutPort> _output;
private protected int _level, _count;
internal PipelineNode(PipelineNodeLabel label, IConvertableToPipelineNode node, bool isNative)
{
_label = label;
_nativeRequested = isNative;
_mainOperation = node;
InputPorts = new();
OutputPorts = new();
foreach (string port in node.InputPortName) {
InputPorts.Add(port, new PipelineNodeInPort(this, port));
}
foreach (string port in node.OutputPortName) {
OutputPorts.Add(port, new PipelineNodeOutPort(this, port));
}
}
#pragma warning disable CS8618
internal PipelineNode(
PipelineNodeOperation operation,
PipelineNodeLabel name,
bool isNative,
string[] inputNames,
string[] outputNames)
#pragma warning restore CS8618
{
if ((inputNames == null) || (inputNames.Length == 0)) {
throw new ArgumentNullException(nameof(inputNames));
}
if ((outputNames == null) || (outputNames.Length == 0)) {
throw new ArgumentNullException(nameof(outputNames));
}
_label = name;
_nativeRequested = isNative;
if (operation == null) {
operation = DefaultOperation;
}
_mainOperation = new DefaultConvertableToBlueprintNode(
operation, inputNames, outputNames);
InputPorts = new Dictionary<string, PipelineNodeInPort>();
OutputPorts = new Dictionary<string, PipelineNodeOutPort>();
foreach (string port in inputNames) {
InputPorts.Add(port, new PipelineNodeInPort(this, port));
}
foreach (string port in outputNames) {
OutputPorts.Add(port, new PipelineNodeOutPort(this, port));
}
}
public bool IsNative
{
get => _nativeRequested;
internal set => _nativeRequested = value;
}
public static Dictionary<string, PipelineConnector> EmptyPortLayer => _emptyLayer;
public IConvertableToPipelineNode NodeOperation => _mainOperation;
public int Level
{
get => _level;
private set => _level = value;
}
public static PipelineNode Empty => _emptyNode;
public PipelineNodeLabel Label => _label;
internal Dictionary<string, PipelineNodeInPort> InputPorts
{
get => _input;
private set => _input = value;
}
internal Dictionary<string, PipelineNodeOutPort> OutputPorts
{
get => _output;
private set => _output = value;
}
internal HashSet<PipelineNode> Previous { get; } = [];
internal HashSet<PipelineNode> Next { get; } = [];
internal IConvertableToPipelineNode Value => _mainOperation;
internal int RemainingIncreaseCount => Previous.Count - _count;
public static void Connect(PipelineNode front, string frontNodePort, string backNodePort, PipelineNode back)
{
ArgumentNullException.ThrowIfNull(front);
ArgumentNullException.ThrowIfNull(back);
if (!front.OutputPorts
.TryGetValue(frontNodePort,
out PipelineNodeOutPort? frontPort)) {
throw new ArgumentOutOfRangeException($"Cannot find port named {frontNodePort}");
}
if (!back.InputPorts.TryGetValue(backNodePort, out PipelineNodeInPort? backPort)) {
throw new ArgumentOutOfRangeException($"Cannot find port named {backNodePort}");
}
backPort.Connect(frontPort);
frontPort.Connect(backPort);
}
public IEnumerator<PipelineNode> GetFormerNodes()
{
foreach (PipelineNodeInPort port in InputPorts.Values)
{
IEnumerator<PipelineNode> enumerator = port.GetFormerNodes();
while (enumerator.MoveNext()) {
yield return enumerator.Current;
}
}
}
public virtual void Run()
{
try
{
// Console.WriteLine(_label);
PipelineNodeInPort.WaitAll(InputPorts);
if (_mainOperation != null) {
_mainOperation.NodeMain(in _input, in _output);
}
}
catch (Exception)
{
throw;
}
}
internal bool TryIncreaseLevelTo(int level)
{
_count++;
if (_count > Previous.Count) {
return false;
}
Level = int.Max(level, this.Level);
return true;
}
protected static void DefaultOperation(
in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output)
{
PipelineNodeOutPort.SkipAll(output);
}
#region port operation
public PipelineNodeInPort GetInputPort(string lable)
{
if (!InputPorts.TryGetValue(lable, out PipelineNodeInPort? ret)) {
throw new ArgumentOutOfRangeException(
$"Cannot find the interface named {lable} in node {Label}.");
}
return ret;
}
public PipelineNodeOutPort GetOutputPort(string lable)
{
if (!OutputPorts.TryGetValue(lable, out PipelineNodeOutPort? ret)) {
throw new ArgumentOutOfRangeException(
$"Cannot find the interface named {lable} in node {Label}.");
}
return ret;
}
public PipelineNodeInPort? DefineCertainInputPort(string label)
{
if (InputPorts.ContainsKey(label)) {
return null;
}
PipelineNodeInPort port = new(this, label);
InputPorts[label] = port;
return port;
}
public PipelineNodeOutPort? DefineCertainOutputPort(string label)
{
if (OutputPorts.ContainsKey(label)) {
return null;
}
PipelineNodeOutPort port = new(this, label);
OutputPorts[label] = port;
return port;
}
#endregion
}//--------------------------------- end of class -----------------------------------
public class PipelineNodeRunException : Exception
{
public PipelineNodeRunException() { }
public PipelineNodeRunException(Exception ex) : base(string.Empty, ex) { }
public PipelineNodeRunException(string message) : base(message) { }
public PipelineNodeRunException(string message, Exception innerException) : base(message, innerException) { }
}
}
+80 -80
View File
@@ -1,80 +1,80 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="PipelineNodeLabel"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.PipeLine
{
public abstract record class PipelineNodeLabel
{
public static PipelineNodeLabel Empty => PipelineNodeLabelEmpty.Only;
public abstract int ComputeHashCode();
public override int GetHashCode()
{
return ComputeHashCode();
}
}
public record class PipelineNodeStringLabel : PipelineNodeLabel
{
public PipelineNodeStringLabel(string name)
{
Name = name;
}
public string Name { get; }
public override int ComputeHashCode()
{
return string.GetHashCode(Name);
}
}
internal record class PipelineNodeLabelEmpty : PipelineNodeLabel
{
public static PipelineNodeLabelEmpty Only { get; } = new PipelineNodeLabelEmpty();
public override int ComputeHashCode()
{
return 0;
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="PipelineNodeLabel"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.PipeLine
{
public abstract record class PipelineNodeLabel
{
public static PipelineNodeLabel Empty => PipelineNodeLabelEmpty.Only;
public abstract int ComputeHashCode();
public override int GetHashCode()
{
return ComputeHashCode();
}
}
public record class PipelineNodeStringLabel : PipelineNodeLabel
{
public PipelineNodeStringLabel(string name)
{
Name = name;
}
public string Name { get; }
public override int ComputeHashCode()
{
return string.GetHashCode(Name);
}
}
internal record class PipelineNodeLabelEmpty : PipelineNodeLabel
{
public static PipelineNodeLabelEmpty Only { get; } = new PipelineNodeLabelEmpty();
public override int ComputeHashCode()
{
return 0;
}
}
}
+187 -187
View File
@@ -1,187 +1,187 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="PipelineNodePort"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.PipeLine
{
public class PipelineNodeInPort
{
private HashSet<PipelineNodeOutPort> _linkerSet = new HashSet<PipelineNodeOutPort>();
public PipelineNodeInPort(PipelineNode container, string label)
{
Container = container;
Label = label;
}
public IEnumerable<PipelineNodeOutPort> LinkedPorts => _linkerSet;
public PipelineNode Container { get; init; }
public SemaphoreSlim Waiter { get; init; }
public string Label { get; init; }
internal HashSet<PipelineNodeOutPort> Linkers => _linkerSet;
public void Connect(PipelineNodeOutPort port)
{
ArgumentNullException.ThrowIfNull(port);
if (_linkerSet.Add(port))
{
port.Linkers.Add(this);
Container.Previous.Add(port.Container);
port.Container.Next.Add(Container);
}
}
public IEnumerator<PipelineNode> GetFormerNodes()
{
foreach (PipelineNodeOutPort port in _linkerSet) {
yield return port.Container;
}
}
public void Wait()
{
PipelineNodeOutPort.WaitAll(_linkerSet);
}
public static void WaitAll(IDictionary<string, PipelineNodeInPort> ports)
{
if (ports is null) {
return;
}
foreach (PipelineNodeInPort port in ports.Values) {
port.Wait();
}
}
public static void WaitAll(IEnumerable<PipelineNodeInPort> ports)
{
if (ports is null) {
return;
}
foreach (PipelineNodeInPort port in ports) {
port.Wait();
}
}
}
public class PipelineNodeOutPort
{
private HashSet<PipelineNodeInPort> _linkerSet = new HashSet<PipelineNodeInPort>();
private volatile int _passCount;
private SemaphoreSlim formerCompleteSemaphore;
public PipelineNodeOutPort(PipelineNode container, string label)
{
Container = container;
Label = label;
formerCompleteSemaphore = new SemaphoreSlim(initialCount:0, maxCount:1);
}
public PipelineNode Container { get; init; }
public string Label { get; init; }
internal HashSet<PipelineNodeInPort> Linkers => _linkerSet;
private SemaphoreSlim Waiter => formerCompleteSemaphore;
public void Connect(PipelineNodeInPort port)
{
ArgumentNullException.ThrowIfNull(port);
if (_linkerSet.Add(port))
{
port.Linkers.Add(this);
Container.Next.Add(port.Container);
port.Container.Previous.Add(Container);
}
}
public void Release()
{
if (_passCount == 0)
{
Waiter.Release();
} else
{
return;
}
}
public static void SkipAll(Dictionary<string, PipelineNodeOutPort> ports)
{
if (ports is null) {
return;
}
foreach (PipelineNodeOutPort port in ports.Values) {
port.Release();
}
}
public void TransmitValue(IPipeLineConnectionPayload value)
{
foreach (PipelineNodeInPort item in _linkerSet) { }
}
public static void WaitAll(IEnumerable<PipelineNodeOutPort> ports)
{
if (ports is null) {
return;
}
foreach (PipelineNodeOutPort port in ports) {
port.Wait();
}
}
internal void Wait()
{
Waiter.Wait();
if (_passCount >= _linkerSet.Count)
{
Interlocked.Exchange(ref _passCount, 0);
} else
{
Interlocked.Increment(ref _passCount);
Waiter.Release();
}
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="PipelineNodePort"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.PipeLine
{
public class PipelineNodeInPort
{
private HashSet<PipelineNodeOutPort> _linkerSet = new HashSet<PipelineNodeOutPort>();
public PipelineNodeInPort(PipelineNode container, string label)
{
Container = container;
Label = label;
}
public IEnumerable<PipelineNodeOutPort> LinkedPorts => _linkerSet;
public PipelineNode Container { get; init; }
public SemaphoreSlim Waiter { get; init; }
public string Label { get; init; }
internal HashSet<PipelineNodeOutPort> Linkers => _linkerSet;
public void Connect(PipelineNodeOutPort port)
{
ArgumentNullException.ThrowIfNull(port);
if (_linkerSet.Add(port))
{
port.Linkers.Add(this);
Container.Previous.Add(port.Container);
port.Container.Next.Add(Container);
}
}
public IEnumerator<PipelineNode> GetFormerNodes()
{
foreach (PipelineNodeOutPort port in _linkerSet) {
yield return port.Container;
}
}
public void Wait()
{
PipelineNodeOutPort.WaitAll(_linkerSet);
}
public static void WaitAll(IDictionary<string, PipelineNodeInPort> ports)
{
if (ports is null) {
return;
}
foreach (PipelineNodeInPort port in ports.Values) {
port.Wait();
}
}
public static void WaitAll(IEnumerable<PipelineNodeInPort> ports)
{
if (ports is null) {
return;
}
foreach (PipelineNodeInPort port in ports) {
port.Wait();
}
}
}
public class PipelineNodeOutPort
{
private HashSet<PipelineNodeInPort> _linkerSet = new HashSet<PipelineNodeInPort>();
private volatile int _passCount;
private SemaphoreSlim formerCompleteSemaphore;
public PipelineNodeOutPort(PipelineNode container, string label)
{
Container = container;
Label = label;
formerCompleteSemaphore = new SemaphoreSlim(initialCount:0, maxCount:1);
}
public PipelineNode Container { get; init; }
public string Label { get; init; }
internal HashSet<PipelineNodeInPort> Linkers => _linkerSet;
private SemaphoreSlim Waiter => formerCompleteSemaphore;
public void Connect(PipelineNodeInPort port)
{
ArgumentNullException.ThrowIfNull(port);
if (_linkerSet.Add(port))
{
port.Linkers.Add(this);
Container.Next.Add(port.Container);
port.Container.Previous.Add(Container);
}
}
public void Release()
{
if (_passCount == 0)
{
Waiter.Release();
} else
{
return;
}
}
public static void SkipAll(Dictionary<string, PipelineNodeOutPort> ports)
{
if (ports is null) {
return;
}
foreach (PipelineNodeOutPort port in ports.Values) {
port.Release();
}
}
public void TransmitValue(IPipeLineConnectionPayload value)
{
foreach (PipelineNodeInPort item in _linkerSet) { }
}
public static void WaitAll(IEnumerable<PipelineNodeOutPort> ports)
{
if (ports is null) {
return;
}
foreach (PipelineNodeOutPort port in ports) {
port.Wait();
}
}
internal void Wait()
{
Waiter.Wait();
if (_passCount >= _linkerSet.Count)
{
Interlocked.Exchange(ref _passCount, 0);
} else
{
Interlocked.Increment(ref _passCount);
Waiter.Release();
}
}
}
}
+295 -295
View File
@@ -1,295 +1,295 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="PipelineScheduler"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Xml;
using System.Xml.Linq;
namespace StgSharp.PipeLine
{
/// <summary>
/// A sets of complex logic designed for running in multi threads environment. Warning: do not
/// run a <see cref="PipelineScheduler" /> instance in multi threads more than once.
/// </summary>
public partial class PipelineScheduler : IConvertableToPipelineNode
{
private BeginningNode _beginning;
private bool _isAvailable = false;
private EndingNode _ending;
private int mtLevel, cLevel,mtIndex, cIndex;
private List<List<PipelineNode>> _concurrentNodes, _mainThreadNodes ;
private SemaphoreSlim runningStat,_concurrentLock = new SemaphoreSlim(1, 1);
internal Dictionary<PipelineNodeLabel, PipelineNode> _allNode;
public PipelineScheduler()
{
_allNode = new Dictionary<PipelineNodeLabel, PipelineNode>();
runningStat = new SemaphoreSlim(1, 1);
_beginning = new BeginningNode(this);
_ending = new EndingNode(this);
}
/**/
public PipelineNode this[PipelineNodeLabel label]
{
get { return _allNode[label]; }
}
public bool IsRunning
{
get => runningStat.CurrentCount == 0;
}
public IEnumerable<string> InputPortName
{
get => _beginning.InputPorts.Keys;
}
public IEnumerable<string> OutputPortName
{
get => _beginning.OutputPorts.Keys;
}
public PipelineNode BeginLayer => _beginning;
public PipelineNode EndLayer => _ending;
public PipelineNodeOperation Operation
{
get => (this as IConvertableToPipelineNode).NodeMain;
}
/**/
internal SemaphoreSlim RunningStat
{
get => runningStat;
}
public void AddNode(PipelineNode node, bool isNative)
{
ArgumentNullException.ThrowIfNull(node);
node.IsNative = isNative;
_allNode.TryAdd(node.Label, node);
}
public void ArrangeNodes()
{
if (_isAvailable) {
return;
}
int maxLevel = 1;
Queue<PipelineNode> completedNodes = new Queue<PipelineNode>();
foreach (PipelineNode item in _beginning.Next)
{
item.TryIncreaseLevelTo(1);
completedNodes.Enqueue(item);
}
// calc level of nodes O(V)
while (completedNodes.Count > 0)
{
PipelineNode u = completedNodes.Dequeue();
foreach (PipelineNode v in u.Next)
{
if (!v.TryIncreaseLevelTo(u.Level + 1)) {
throw new InvalidOperationException("Ring in DAG detected.");
}
if (v.RemainingIncreaseCount == 0)
{
completedNodes.Enqueue(v);
maxLevel = int.Max(maxLevel, v.Level);
}
}
}
// bucket sort of nodes, O(E)
_concurrentNodes = new List<List<PipelineNode>>(maxLevel + 1);
_mainThreadNodes = new List<List<PipelineNode>>(maxLevel + 1);
for (int i = 0; i <= maxLevel; i++)
{
_concurrentNodes.Add([]);
_mainThreadNodes.Add([]);
}
foreach (PipelineNode item in _allNode.Values)
{
if (item.IsNative)
{
_mainThreadNodes[item.Level].Add(item);
} else
{
_concurrentNodes[item.Level].Add(item);
}
}
foreach (List<PipelineNode> item in _concurrentNodes) {
item.TrimExcess();
}
foreach (List<PipelineNode> item in _mainThreadNodes) {
item.TrimExcess();
}
_isAvailable = true;
}
public PipelineNode Create(PipelineNodeLabel label, [NotNull] IConvertableToPipelineNode body)
{
if (_allNode.TryGetValue(label, out PipelineNode? node)) {
throw new InvalidOperationException("Label has exist in scheduler.");
}
node = new PipelineNode(label, body, false);
_allNode.Add(label, node);
return node;
}
public PipelineNode GetNode(PipelineNodeLabel label)
{
if (_allNode.TryGetValue(label, out PipelineNode? node))
{
return node;
} else
{
throw new KeyNotFoundException();
}
}
public void LinkToInput(PipelineNodeInPort port)
{
ArgumentNullException.ThrowIfNull(port);
PipelineNodeOutPort p = _beginning.DefaultOut;
p.Connect(port);
}
public void LinkToInput(PipelineNodeInPort port, string inputPortName)
{
ArgumentNullException.ThrowIfNull(port);
PipelineNodeOutPort p = _beginning.DefineCertainOutputPort(inputPortName);
p.Connect(port);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetInput(params (string label, IPipeLineConnectionPayload arg)[] parameters)
{
_beginning.SetInputData(parameters);
}
public void Terminate()
{
_concurrentLock.Wait();
(mtLevel, cLevel, mtIndex, cIndex) = (int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue);
_concurrentLock.Release();
}
public bool TryGetAllNode(
out List<List<PipelineNode>> mainThreadNodes,
out List<List<PipelineNode>> concurrentNodes)
{
if (_isAvailable)
{
mainThreadNodes = _mainThreadNodes;
concurrentNodes = _concurrentNodes;
return true;
}
mainThreadNodes = null;
concurrentNodes = null;
return false;
}
internal bool RequestNexConcurrentNode(out PipelineNode node)
{
int index, level;
if (cLevel >= _concurrentNodes.Count)
{
node = null!;
return false;
}
_concurrentLock.Wait();
while (cIndex >= _concurrentNodes[cLevel].Count)
{
Interlocked.Increment(ref cLevel);
Interlocked.Exchange(ref cIndex, 0);
if (cLevel >= _concurrentNodes.Count)
{
node = null!;
return false;
}
}
index = cIndex;
level = cLevel;
Interlocked.Increment(ref cIndex);
_concurrentLock.Release();
node = _concurrentNodes[level][index];
return true;
}
internal bool RequestNextMainThreadNode(out PipelineNode node)
{
if (mtLevel >= _mainThreadNodes.Count)
{
node = null!;
return false;
}
while (mtIndex >= _mainThreadNodes[cLevel].Count)
{
Interlocked.Increment(ref mtLevel);
Interlocked.Exchange(ref mtIndex, 0);
if (mtLevel >= _mainThreadNodes.Count)
{
node = null!;
return false;
}
}
node = _mainThreadNodes[mtLevel][mtIndex];
return true;
}
internal void ResetIndex()
{
(mtLevel, cLevel, mtIndex, cIndex) = (0, 0, 0, 0);
_concurrentLock.Release();
}
void IConvertableToPipelineNode.NodeMain(
in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output)
{
PipeLineRunner.Run(this);
PipelineNodeOutPort.SkipAll(output);
}
~PipelineScheduler()
{
runningStat.Dispose();
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="PipelineScheduler"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Xml;
using System.Xml.Linq;
namespace StgSharp.PipeLine
{
/// <summary>
/// A sets of complex logic designed for running in multi threads environment. Warning: do not
/// run a <see cref="PipelineScheduler" /> instance in multi threads more than once.
/// </summary>
public partial class PipelineScheduler : IConvertableToPipelineNode
{
private BeginningNode _beginning;
private bool _isAvailable = false;
private EndingNode _ending;
private int mtLevel, cLevel,mtIndex, cIndex;
private List<List<PipelineNode>> _concurrentNodes, _mainThreadNodes ;
private SemaphoreSlim runningStat,_concurrentLock = new SemaphoreSlim(1, 1);
internal Dictionary<PipelineNodeLabel, PipelineNode> _allNode;
public PipelineScheduler()
{
_allNode = new Dictionary<PipelineNodeLabel, PipelineNode>();
runningStat = new SemaphoreSlim(1, 1);
_beginning = new BeginningNode(this);
_ending = new EndingNode(this);
}
/**/
public PipelineNode this[PipelineNodeLabel label]
{
get { return _allNode[label]; }
}
public bool IsRunning
{
get => runningStat.CurrentCount == 0;
}
public IEnumerable<string> InputPortName
{
get => _beginning.InputPorts.Keys;
}
public IEnumerable<string> OutputPortName
{
get => _beginning.OutputPorts.Keys;
}
public PipelineNode BeginLayer => _beginning;
public PipelineNode EndLayer => _ending;
public PipelineNodeOperation Operation
{
get => (this as IConvertableToPipelineNode).NodeMain;
}
/**/
internal SemaphoreSlim RunningStat
{
get => runningStat;
}
public void AddNode(PipelineNode node, bool isNative)
{
ArgumentNullException.ThrowIfNull(node);
node.IsNative = isNative;
_allNode.TryAdd(node.Label, node);
}
public void ArrangeNodes()
{
if (_isAvailable) {
return;
}
int maxLevel = 1;
Queue<PipelineNode> completedNodes = new Queue<PipelineNode>();
foreach (PipelineNode item in _beginning.Next)
{
item.TryIncreaseLevelTo(1);
completedNodes.Enqueue(item);
}
// calc level of nodes O(V)
while (completedNodes.Count > 0)
{
PipelineNode u = completedNodes.Dequeue();
foreach (PipelineNode v in u.Next)
{
if (!v.TryIncreaseLevelTo(u.Level + 1)) {
throw new InvalidOperationException("Ring in DAG detected.");
}
if (v.RemainingIncreaseCount == 0)
{
completedNodes.Enqueue(v);
maxLevel = int.Max(maxLevel, v.Level);
}
}
}
// bucket sort of nodes, O(E)
_concurrentNodes = new List<List<PipelineNode>>(maxLevel + 1);
_mainThreadNodes = new List<List<PipelineNode>>(maxLevel + 1);
for (int i = 0; i <= maxLevel; i++)
{
_concurrentNodes.Add([]);
_mainThreadNodes.Add([]);
}
foreach (PipelineNode item in _allNode.Values)
{
if (item.IsNative)
{
_mainThreadNodes[item.Level].Add(item);
} else
{
_concurrentNodes[item.Level].Add(item);
}
}
foreach (List<PipelineNode> item in _concurrentNodes) {
item.TrimExcess();
}
foreach (List<PipelineNode> item in _mainThreadNodes) {
item.TrimExcess();
}
_isAvailable = true;
}
public PipelineNode Create(PipelineNodeLabel label, [NotNull] IConvertableToPipelineNode body)
{
if (_allNode.TryGetValue(label, out PipelineNode? node)) {
throw new InvalidOperationException("Label has exist in scheduler.");
}
node = new PipelineNode(label, body, false);
_allNode.Add(label, node);
return node;
}
public PipelineNode GetNode(PipelineNodeLabel label)
{
if (_allNode.TryGetValue(label, out PipelineNode? node))
{
return node;
} else
{
throw new KeyNotFoundException();
}
}
public void LinkToInput(PipelineNodeInPort port)
{
ArgumentNullException.ThrowIfNull(port);
PipelineNodeOutPort p = _beginning.DefaultOut;
p.Connect(port);
}
public void LinkToInput(PipelineNodeInPort port, string inputPortName)
{
ArgumentNullException.ThrowIfNull(port);
PipelineNodeOutPort p = _beginning.DefineCertainOutputPort(inputPortName);
p.Connect(port);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetInput(params (string label, IPipeLineConnectionPayload arg)[] parameters)
{
_beginning.SetInputData(parameters);
}
public void Terminate()
{
_concurrentLock.Wait();
(mtLevel, cLevel, mtIndex, cIndex) = (int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue);
_concurrentLock.Release();
}
public bool TryGetAllNode(
out List<List<PipelineNode>> mainThreadNodes,
out List<List<PipelineNode>> concurrentNodes)
{
if (_isAvailable)
{
mainThreadNodes = _mainThreadNodes;
concurrentNodes = _concurrentNodes;
return true;
}
mainThreadNodes = null;
concurrentNodes = null;
return false;
}
internal bool RequestNexConcurrentNode(out PipelineNode node)
{
int index, level;
if (cLevel >= _concurrentNodes.Count)
{
node = null!;
return false;
}
_concurrentLock.Wait();
while (cIndex >= _concurrentNodes[cLevel].Count)
{
Interlocked.Increment(ref cLevel);
Interlocked.Exchange(ref cIndex, 0);
if (cLevel >= _concurrentNodes.Count)
{
node = null!;
return false;
}
}
index = cIndex;
level = cLevel;
Interlocked.Increment(ref cIndex);
_concurrentLock.Release();
node = _concurrentNodes[level][index];
return true;
}
internal bool RequestNextMainThreadNode(out PipelineNode node)
{
if (mtLevel >= _mainThreadNodes.Count)
{
node = null!;
return false;
}
while (mtIndex >= _mainThreadNodes[cLevel].Count)
{
Interlocked.Increment(ref mtLevel);
Interlocked.Exchange(ref mtIndex, 0);
if (mtLevel >= _mainThreadNodes.Count)
{
node = null!;
return false;
}
}
node = _mainThreadNodes[mtLevel][mtIndex];
return true;
}
internal void ResetIndex()
{
(mtLevel, cLevel, mtIndex, cIndex) = (0, 0, 0, 0);
_concurrentLock.Release();
}
void IConvertableToPipelineNode.NodeMain(
in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output)
{
PipeLineRunner.Run(this);
PipelineNodeOutPort.SkipAll(output);
}
~PipelineScheduler()
{
runningStat.Dispose();
}
}
}
+123 -123
View File
@@ -1,123 +1,123 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="PortArgs"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Security.Authentication.ExtendedProtection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.PipeLine
{
public class PipelineConnector
{
private protected IPipeLineConnectionPayload args;
private protected PipelineNodeInPort output;
private protected PipelineNodeOutPort input;
private protected SemaphoreSlim formerCompleteSemaphore;
public PipelineConnector(PipelineNodeOutPort former, PipelineNodeInPort after)
{
this.input = former;
this.output = after;
formerCompleteSemaphore = new SemaphoreSlim(initialCount:0, maxCount:1);
}
public IPipeLineConnectionPayload Args
{
get => args;
set => args = value;
}
public PipelineNodeInPort Output => output;
public PipelineNodeOutPort Input => input;
public static void SkipAll(Dictionary<string, PipelineConnector> ports)
{
if (ports == null) {
return;
}
foreach (KeyValuePair<string, PipelineConnector> item in ports)
{
if (item.Value != null) {
item.Value.CompleteAndSkip();
}
}
}
public static void WaitAll(Dictionary<string, PipelineConnector> ports)
{
if (ports == null) {
return;
}
foreach (KeyValuePair<string, PipelineConnector> item in ports)
{
if (item.Value != null) {
item.Value.WaitForStart();
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual void WaitForStart()
{
// Console.WriteLine($"Waiting \t{input.ContextName}\t->\t{output.ContextName}");
formerCompleteSemaphore.Wait();
}
~PipelineConnector()
{
formerCompleteSemaphore.Dispose();
}
#region pipline op
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CompleteAndSkip()
{
// Console.WriteLine($"Complete\t{input.Name}\t->\t{output.Name}");
formerCompleteSemaphore.Release();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CompleteAndWriteData(IPipeLineConnectionPayload args)
{
this.args = args;
CompleteAndSkip();
}
#endregion
}
public abstract class IPipeLineConnectionPayload { }
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="PortArgs"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Security.Authentication.ExtendedProtection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.PipeLine
{
public class PipelineConnector
{
private protected IPipeLineConnectionPayload args;
private protected PipelineNodeInPort output;
private protected PipelineNodeOutPort input;
private protected SemaphoreSlim formerCompleteSemaphore;
public PipelineConnector(PipelineNodeOutPort former, PipelineNodeInPort after)
{
this.input = former;
this.output = after;
formerCompleteSemaphore = new SemaphoreSlim(initialCount:0, maxCount:1);
}
public IPipeLineConnectionPayload Args
{
get => args;
set => args = value;
}
public PipelineNodeInPort Output => output;
public PipelineNodeOutPort Input => input;
public static void SkipAll(Dictionary<string, PipelineConnector> ports)
{
if (ports == null) {
return;
}
foreach (KeyValuePair<string, PipelineConnector> item in ports)
{
if (item.Value != null) {
item.Value.CompleteAndSkip();
}
}
}
public static void WaitAll(Dictionary<string, PipelineConnector> ports)
{
if (ports == null) {
return;
}
foreach (KeyValuePair<string, PipelineConnector> item in ports)
{
if (item.Value != null) {
item.Value.WaitForStart();
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual void WaitForStart()
{
// Console.WriteLine($"Waiting \t{input.ContextName}\t->\t{output.ContextName}");
formerCompleteSemaphore.Wait();
}
~PipelineConnector()
{
formerCompleteSemaphore.Dispose();
}
#region pipline op
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CompleteAndSkip()
{
// Console.WriteLine($"Complete\t{input.Name}\t->\t{output.Name}");
formerCompleteSemaphore.Release();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CompleteAndWriteData(IPipeLineConnectionPayload args)
{
this.args = args;
CompleteAndSkip();
}
#endregion
}
public abstract class IPipeLineConnectionPayload { }
}
+92 -92
View File
@@ -1,93 +1,93 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SideNode"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.PipeLine
{
internal sealed class BeginningNode : PipelineNode
{
public const string OutName = "default";
private PipelineScheduler _bp;
public BeginningNode(PipelineScheduler bp)
: base(
DefaultOperation, new PipelineNodeStringLabel("BeginningNode"), true, [ "default" ],[ OutName ])
{
_bp = bp;
}
public PipelineNodeOutPort DefaultOut => OutputPorts[OutName];
public override void Run()
{
_bp.RunningStat.Wait();
PipelineNodeOutPort.SkipAll(_output);
}
internal void SetInputData(IEnumerable<(string, IPipeLineConnectionPayload)> data)
{
if (data is null) {
return;
}
foreach ((string, IPipeLineConnectionPayload) item in data) {
OutputPorts[item.Item1].TransmitValue(item.Item2);
}
}
}
internal sealed class EndingNode : PipelineNode
{
private PipelineScheduler _bp;
public EndingNode(PipelineScheduler bp)
: base(
DefaultOperation, name:new PipelineNodeStringLabel("EndingNode"), true, [ "default" ],[ "default" ])
{
_bp = bp;
}
public override void Run()
{
PipelineNodeInPort.WaitAll(_input);
_bp.ResetIndex();
_bp.RunningStat.Release();
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SideNode"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.PipeLine
{
internal sealed class BeginningNode : PipelineNode
{
public const string OutName = "default";
private PipelineScheduler _bp;
public BeginningNode(PipelineScheduler bp)
: base(
DefaultOperation, new PipelineNodeStringLabel("BeginningNode"), true, [ "default" ],[ OutName ])
{
_bp = bp;
}
public PipelineNodeOutPort DefaultOut => OutputPorts[OutName];
public override void Run()
{
_bp.RunningStat.Wait();
PipelineNodeOutPort.SkipAll(_output);
}
internal void SetInputData(IEnumerable<(string, IPipeLineConnectionPayload)> data)
{
if (data is null) {
return;
}
foreach ((string, IPipeLineConnectionPayload) item in data) {
OutputPorts[item.Item1].TransmitValue(item.Item2);
}
}
}
internal sealed class EndingNode : PipelineNode
{
private PipelineScheduler _bp;
public EndingNode(PipelineScheduler bp)
: base(
DefaultOperation, name:new PipelineNodeStringLabel("EndingNode"), true, [ "default" ],[ "default" ])
{
_bp = bp;
}
public override void Run()
{
PipelineNodeInPort.WaitAll(_input);
_bp.ResetIndex();
_bp.RunningStat.Release();
}
}
}
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>x64</Platform>
<PublishDir>C:\Users\Nitlo\OneDrive - MSFT\桌面\新建文件夹</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<PublishReadyToRun>true</PublishReadyToRun>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>x64</Platform>
<PublishDir>C:\Users\Nitlo\OneDrive - MSFT\桌面\新建文件夹</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<PublishReadyToRun>true</PublishReadyToRun>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
</Project>
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<History>False|2025-08-24T07:15:39.9650284Z||;</History>
<LastFailureDetails />
</PropertyGroup>
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<History>False|2025-08-24T07:15:39.9650284Z||;</History>
<LastFailureDetails />
</PropertyGroup>
</Project>
+56 -56
View File
@@ -1,56 +1,56 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="ISimpleStateMachine"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.State
{
public interface ISimpleStateMachine<TStateNode, TStateLabel, TStateContext>
where TStateNode : SimpleStateMachineNode<TStateNode, TStateLabel, TStateContext>
where TStateLabel : unmanaged, IEquatable<TStateLabel>
where TStateContext : StateContext
{
TStateNode this[
TStateLabel label
] { get; }
ref readonly TStateNode GetCurrentStateNode();
void Reset();
bool TryMoveNext(
TStateContext ctx
);
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="ISimpleStateMachine"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.State
{
public interface ISimpleStateMachine<TStateNode, TStateLabel, TStateContext>
where TStateNode : SimpleStateMachineNode<TStateNode, TStateLabel, TStateContext>
where TStateLabel : unmanaged, IEquatable<TStateLabel>
where TStateContext : StateContext
{
TStateNode this[
TStateLabel label
] { get; }
ref readonly TStateNode GetCurrentStateNode();
void Reset();
bool TryMoveNext(
TStateContext ctx
);
}
}
+60 -60
View File
@@ -1,60 +1,60 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SimpleStateMachineNode"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.State
{
public abstract class SimpleStateMachineNode<TSelf, TLabel, TStateContext>
where TSelf : SimpleStateMachineNode<TSelf, TLabel, TStateContext>
where TLabel : unmanaged, IEquatable<TLabel>
where TStateContext : StateContext
{
private readonly List<int> _jumpTable = [];
public ReadOnlySpan<int> AvailableNodes => CollectionsMarshal.AsSpan(_jumpTable);
public abstract bool IsValidateFrom(
TSelf former,
TStateContext context
);
public abstract bool TryTransaction(
ReadOnlySpan<TSelf> nodeSpan,
TStateContext context,
out TLabel label
);
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SimpleStateMachineNode"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.State
{
public abstract class SimpleStateMachineNode<TSelf, TLabel, TStateContext>
where TSelf : SimpleStateMachineNode<TSelf, TLabel, TStateContext>
where TLabel : unmanaged, IEquatable<TLabel>
where TStateContext : StateContext
{
private readonly List<int> _jumpTable = [];
public ReadOnlySpan<int> AvailableNodes => CollectionsMarshal.AsSpan(_jumpTable);
public abstract bool IsValidateFrom(
TSelf former,
TStateContext context
);
public abstract bool TryTransaction(
ReadOnlySpan<TSelf> nodeSpan,
TStateContext context,
out TLabel label
);
}
}
+40 -40
View File
@@ -1,40 +1,40 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="StateContext.cs"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.State
{
public abstract class StateContext { }
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="StateContext.cs"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.State
{
public abstract class StateContext { }
}
+29 -29
View File
@@ -1,30 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<IsNativeHost>true</IsNativeHost>
</PropertyGroup>
<ItemGroup Condition="'$(IsWindows)'=='true'">
<ProjectReference Include="..\StgSharp.Native\$(NativeLibName).vcxproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
<GlobalPropertiesToRemove>TargetFramework</GlobalPropertiesToRemove>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Graphics\**" />
<EmbeddedResource Remove="Graphics\**" />
<None Remove="Graphics\**" />
</ItemGroup>
<ItemGroup>
<Folder Include="HighPerformance\RegisterAbstraction\" />
</ItemGroup>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<IsNativeHost>true</IsNativeHost>
</PropertyGroup>
<ItemGroup Condition="'$(IsWindows)'=='true'">
<ProjectReference Include="..\StgSharp.Native\$(NativeLibName).vcxproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
<GlobalPropertiesToRemove>TargetFramework</GlobalPropertiesToRemove>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Graphics\**" />
<EmbeddedResource Remove="Graphics\**" />
<None Remove="Graphics\**" />
</ItemGroup>
<ItemGroup>
<Folder Include="HighPerformance\RegisterAbstraction\" />
</ItemGroup>
</Project>
+5 -5
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<_LastSelectedProfileId>D:\NitloadIndustry\研究项目\1-Flagship\TH2020\ProjectFile\内核源代码\StgSharp.Common\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
</PropertyGroup>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<_LastSelectedProfileId>D:\NitloadIndustry\研究项目\1-Flagship\TH2020\ProjectFile\内核源代码\StgSharp.Common\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
</PropertyGroup>
</Project>
@@ -1,366 +1,366 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="AdaptiveBackoffStrategy"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
/// <summary>
/// Efficient adaptive backoff strategy that dynamically selects optimal backoff methods based on
/// system load and success rate
/// </summary>
public struct AdaptiveBackoffStrategy
{
private const int EVALUATION_INTERVAL_MS = 2000; // Global re-evaluation interval
private const int EXPLORATION_PROBABILITY = 10; // 10% probability to explore non-optimal strategies
private const int FAST_CONVERGENCE_THRESHOLD = 1000; // Fast convergence threshold
// Configuration constants
private const int INITIAL_SPINWAIT_THRESHOLD = 8; // Cheap spin count for first few SpinWait iterations
private const int MIN_SAMPLES_FOR_EVALUATION = 100; // Minimum sample count before starting evaluation
// Global shared state (all threads share learning results)
private static readonly PerformanceStats[] s_globalStats = new PerformanceStats[3];
private static volatile BackoffMethod s_globalBestMethod;
private static volatile int s_evaluationInProgress;
private static long s_lastGlobalEvaluationTicks;
private BackoffMethod _currentMethod;
private int _consecutiveFailures;
private int _localAttempts;
// Instance state
private SpinWait _spinWait;
private uint _fastRng; // Lightweight pseudo-random number generator
// Static constructor: initialize optimal strategy based on system characteristics
static AdaptiveBackoffStrategy()
{
// Based on your benchmark results:
// - Low core count (<= 8): SpinWait has clear advantage
// - Medium core count (<= 16): Close performance, slightly favor SpinWait
// - High core count (>= 20): Sleep0 has significant advantage under high concurrency
int logicalCores = Environment.ProcessorCount;
if (logicalCores <= 8)
{
s_globalBestMethod = BackoffMethod.SpinWait;
} else if (logicalCores <= 16)
{
s_globalBestMethod = BackoffMethod.SpinWait; // Default conservative choice
} else
{
// High core count systems, based on your 64-thread benchmark results
s_globalBestMethod = BackoffMethod.Sleep0;
}
s_lastGlobalEvaluationTicks = Environment.TickCount64;
// Initialize statistics array
for (int i = 0; i < s_globalStats.Length; i++)
{
s_globalStats[i].Reset();
}
}
/// <summary>
/// Gets the currently used backoff method (for debugging/monitoring)
/// </summary>
public readonly BackoffMethod CurrentMethod => _currentMethod;
/// <summary>
/// Gets the globally considered optimal backoff method
/// </summary>
public static BackoffMethod GlobalBestMethod => s_globalBestMethod;
/// <summary>
/// Creates a new adaptive backoff strategy instance
/// </summary>
public static AdaptiveBackoffStrategy Create()
{
return new AdaptiveBackoffStrategy
{
_spinWait = default,
_currentMethod = s_globalBestMethod,
_consecutiveFailures = 0,
_localAttempts = 0,
_fastRng = (uint)(Environment.TickCount64 ^ Thread.CurrentThread.ManagedThreadId * 0x9E3779B9U)
};
}
/// <summary>
/// Gets global statistics for the specified strategy (for monitoring)
/// </summary>
public static (long attempts, long successes, double successRate) GetGlobalStats(BackoffMethod method)
{
PerformanceStats stats = s_globalStats[(int)method];
return (stats.Attempts, stats.Successes, stats.SuccessRate);
}
/// <summary>
/// Called after CAS failure, performs backoff and may switch strategies
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnFailureAndBackoff()
{
_consecutiveFailures++;
// Record failure
s_globalStats[(int)_currentMethod].RecordAttempt(success:false);
// Periodically re-evaluate global optimal strategy
if (ShouldEvaluateGlobalStrategy())
{
EvaluateAndUpdateGlobalStrategy();
}
// Decide whether to switch strategies based on failure count and current strategy
if (_consecutiveFailures >= INITIAL_SPINWAIT_THRESHOLD && ShouldSwitchStrategy())
{
SwitchToOptimalStrategy();
}
// Perform the actual backoff operation
PerformBackoff();
}
/// <summary>
/// Called after CAS success, resets state
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnSuccess()
{
// Record success
s_globalStats[(int)_currentMethod].RecordAttempt(success:true);
// Reset state
_spinWait = default;
_consecutiveFailures = 0;
// Fast convergence: tend to keep current strategy when continuously successful
if (_localAttempts < FAST_CONVERGENCE_THRESHOLD)
{
_localAttempts++;
}
}
/// <summary>
/// Evaluates and updates the global optimal strategy
/// </summary>
private static void EvaluateAndUpdateGlobalStrategy()
{
// Use CAS to ensure only one thread performs evaluation
if (Interlocked.CompareExchange(ref s_evaluationInProgress, 1, 0) != 0)
{
return;
}
try
{
long now = Environment.TickCount64;
// Double-check to avoid duplicate evaluation
if (now - Volatile.Read(ref s_lastGlobalEvaluationTicks) <= EVALUATION_INTERVAL_MS)
{
return;
}
BackoffMethod bestMethod = s_globalBestMethod;
double bestSuccessRate = -1.0;
// Find the strategy with the highest success rate
for (int i = 0; i < s_globalStats.Length; i++)
{
PerformanceStats stats = s_globalStats[i];
if (stats.Attempts >= MIN_SAMPLES_FOR_EVALUATION)
{
double successRate = stats.SuccessRate;
if (successRate > bestSuccessRate)
{
bestSuccessRate = successRate;
bestMethod = (BackoffMethod)i;
}
}
}
// Update global optimal strategy
s_globalBestMethod = bestMethod;
Volatile.Write(ref s_lastGlobalEvaluationTicks, now);
// Reset statistics (maintain exploratory behavior)
for (int i = 0; i < s_globalStats.Length; i++)
{
s_globalStats[i].Reset();
}
}
finally
{
Volatile.Write(ref s_evaluationInProgress, 0);
}
}
/// <summary>
/// Fast pseudo-random number generator (LCG)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private uint NextRandom()
{
_fastRng = _fastRng * 1103515245U + 12345U;
return _fastRng;
}
/// <summary>
/// Performs the actual backoff operation
/// </summary>
[MethodImpl(MethodImplOptions.NoInlining)] // Avoid inlining to reduce hot path code size
private void PerformBackoff()
{
switch (_currentMethod)
{
case BackoffMethod.SpinWait:
if (_consecutiveFailures <= INITIAL_SPINWAIT_THRESHOLD)
{
_spinWait.SpinOnce(sleep1Threshold:-1);
} else if (_spinWait.NextSpinWillYield)
{
int yields = (int)(NextRandom() & 7) + 1; // 1-8 times
for (int i = 0; i < yields; i++) {
_ = Thread.Yield();
}
} else
{
_spinWait.SpinOnce(sleep1Threshold:-1);
}
break;
case BackoffMethod.Yield:
_ = Thread.Yield();
break;
case BackoffMethod.Sleep0:
Thread.Sleep(0);
break;
default:
break;
}
}
/// <summary>
/// Checks whether global strategy evaluation should be performed
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool ShouldEvaluateGlobalStrategy()
{
long now = Environment.TickCount64;
return now - Volatile.Read(ref s_lastGlobalEvaluationTicks) > EVALUATION_INTERVAL_MS;
}
/// <summary>
/// Determines whether strategy should be switched
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool ShouldSwitchStrategy()
{
// Fast convergence phase: reduce strategy switching
if (_localAttempts < FAST_CONVERGENCE_THRESHOLD && _consecutiveFailures < 32)
{
return false;
}
// Small probability exploration: occasionally try other strategies even when current is optimal
return NextRandom() % 100 < EXPLORATION_PROBABILITY || _currentMethod != s_globalBestMethod;
}
/// <summary>
/// Switches to the currently considered optimal strategy
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SwitchToOptimalStrategy()
{
BackoffMethod targetMethod;
// Most of the time choose the globally optimal
if (NextRandom() % 100 >= EXPLORATION_PROBABILITY)
{
targetMethod = s_globalBestMethod;
} else
{
// Small probability random exploration
targetMethod = (BackoffMethod)(NextRandom() % 3);
}
if (targetMethod != _currentMethod)
{
_currentMethod = targetMethod;
_spinWait = default; // Reset SpinWait state when switching strategies
_localAttempts = 0;
}
}
public enum BackoffMethod : byte
{
SpinWait = 0,
Yield = 1,
Sleep0 = 2
}
private struct PerformanceStats
{
public long Attempts;
public long LastResetTicks;
public long Successes;
public readonly double SuccessRate
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Attempts == 0 ? 0.0 : (double)Successes / Attempts;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RecordAttempt(bool success)
{
_ = Interlocked.Increment(ref Attempts);
if (success) {
_ = Interlocked.Increment(ref Successes);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset()
{
Attempts = 0;
Successes = 0;
LastResetTicks = Environment.TickCount64;
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="AdaptiveBackoffStrategy"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
/// <summary>
/// Efficient adaptive backoff strategy that dynamically selects optimal backoff methods based on
/// system load and success rate
/// </summary>
public struct AdaptiveBackoffStrategy
{
private const int EVALUATION_INTERVAL_MS = 2000; // Global re-evaluation interval
private const int EXPLORATION_PROBABILITY = 10; // 10% probability to explore non-optimal strategies
private const int FAST_CONVERGENCE_THRESHOLD = 1000; // Fast convergence threshold
// Configuration constants
private const int INITIAL_SPINWAIT_THRESHOLD = 8; // Cheap spin count for first few SpinWait iterations
private const int MIN_SAMPLES_FOR_EVALUATION = 100; // Minimum sample count before starting evaluation
// Global shared state (all threads share learning results)
private static readonly PerformanceStats[] s_globalStats = new PerformanceStats[3];
private static volatile BackoffMethod s_globalBestMethod;
private static volatile int s_evaluationInProgress;
private static long s_lastGlobalEvaluationTicks;
private BackoffMethod _currentMethod;
private int _consecutiveFailures;
private int _localAttempts;
// Instance state
private SpinWait _spinWait;
private uint _fastRng; // Lightweight pseudo-random number generator
// Static constructor: initialize optimal strategy based on system characteristics
static AdaptiveBackoffStrategy()
{
// Based on your benchmark results:
// - Low core count (<= 8): SpinWait has clear advantage
// - Medium core count (<= 16): Close performance, slightly favor SpinWait
// - High core count (>= 20): Sleep0 has significant advantage under high concurrency
int logicalCores = Environment.ProcessorCount;
if (logicalCores <= 8)
{
s_globalBestMethod = BackoffMethod.SpinWait;
} else if (logicalCores <= 16)
{
s_globalBestMethod = BackoffMethod.SpinWait; // Default conservative choice
} else
{
// High core count systems, based on your 64-thread benchmark results
s_globalBestMethod = BackoffMethod.Sleep0;
}
s_lastGlobalEvaluationTicks = Environment.TickCount64;
// Initialize statistics array
for (int i = 0; i < s_globalStats.Length; i++)
{
s_globalStats[i].Reset();
}
}
/// <summary>
/// Gets the currently used backoff method (for debugging/monitoring)
/// </summary>
public readonly BackoffMethod CurrentMethod => _currentMethod;
/// <summary>
/// Gets the globally considered optimal backoff method
/// </summary>
public static BackoffMethod GlobalBestMethod => s_globalBestMethod;
/// <summary>
/// Creates a new adaptive backoff strategy instance
/// </summary>
public static AdaptiveBackoffStrategy Create()
{
return new AdaptiveBackoffStrategy
{
_spinWait = default,
_currentMethod = s_globalBestMethod,
_consecutiveFailures = 0,
_localAttempts = 0,
_fastRng = (uint)(Environment.TickCount64 ^ Thread.CurrentThread.ManagedThreadId * 0x9E3779B9U)
};
}
/// <summary>
/// Gets global statistics for the specified strategy (for monitoring)
/// </summary>
public static (long attempts, long successes, double successRate) GetGlobalStats(BackoffMethod method)
{
PerformanceStats stats = s_globalStats[(int)method];
return (stats.Attempts, stats.Successes, stats.SuccessRate);
}
/// <summary>
/// Called after CAS failure, performs backoff and may switch strategies
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnFailureAndBackoff()
{
_consecutiveFailures++;
// Record failure
s_globalStats[(int)_currentMethod].RecordAttempt(success:false);
// Periodically re-evaluate global optimal strategy
if (ShouldEvaluateGlobalStrategy())
{
EvaluateAndUpdateGlobalStrategy();
}
// Decide whether to switch strategies based on failure count and current strategy
if (_consecutiveFailures >= INITIAL_SPINWAIT_THRESHOLD && ShouldSwitchStrategy())
{
SwitchToOptimalStrategy();
}
// Perform the actual backoff operation
PerformBackoff();
}
/// <summary>
/// Called after CAS success, resets state
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnSuccess()
{
// Record success
s_globalStats[(int)_currentMethod].RecordAttempt(success:true);
// Reset state
_spinWait = default;
_consecutiveFailures = 0;
// Fast convergence: tend to keep current strategy when continuously successful
if (_localAttempts < FAST_CONVERGENCE_THRESHOLD)
{
_localAttempts++;
}
}
/// <summary>
/// Evaluates and updates the global optimal strategy
/// </summary>
private static void EvaluateAndUpdateGlobalStrategy()
{
// Use CAS to ensure only one thread performs evaluation
if (Interlocked.CompareExchange(ref s_evaluationInProgress, 1, 0) != 0)
{
return;
}
try
{
long now = Environment.TickCount64;
// Double-check to avoid duplicate evaluation
if (now - Volatile.Read(ref s_lastGlobalEvaluationTicks) <= EVALUATION_INTERVAL_MS)
{
return;
}
BackoffMethod bestMethod = s_globalBestMethod;
double bestSuccessRate = -1.0;
// Find the strategy with the highest success rate
for (int i = 0; i < s_globalStats.Length; i++)
{
PerformanceStats stats = s_globalStats[i];
if (stats.Attempts >= MIN_SAMPLES_FOR_EVALUATION)
{
double successRate = stats.SuccessRate;
if (successRate > bestSuccessRate)
{
bestSuccessRate = successRate;
bestMethod = (BackoffMethod)i;
}
}
}
// Update global optimal strategy
s_globalBestMethod = bestMethod;
Volatile.Write(ref s_lastGlobalEvaluationTicks, now);
// Reset statistics (maintain exploratory behavior)
for (int i = 0; i < s_globalStats.Length; i++)
{
s_globalStats[i].Reset();
}
}
finally
{
Volatile.Write(ref s_evaluationInProgress, 0);
}
}
/// <summary>
/// Fast pseudo-random number generator (LCG)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private uint NextRandom()
{
_fastRng = _fastRng * 1103515245U + 12345U;
return _fastRng;
}
/// <summary>
/// Performs the actual backoff operation
/// </summary>
[MethodImpl(MethodImplOptions.NoInlining)] // Avoid inlining to reduce hot path code size
private void PerformBackoff()
{
switch (_currentMethod)
{
case BackoffMethod.SpinWait:
if (_consecutiveFailures <= INITIAL_SPINWAIT_THRESHOLD)
{
_spinWait.SpinOnce(sleep1Threshold:-1);
} else if (_spinWait.NextSpinWillYield)
{
int yields = (int)(NextRandom() & 7) + 1; // 1-8 times
for (int i = 0; i < yields; i++) {
_ = Thread.Yield();
}
} else
{
_spinWait.SpinOnce(sleep1Threshold:-1);
}
break;
case BackoffMethod.Yield:
_ = Thread.Yield();
break;
case BackoffMethod.Sleep0:
Thread.Sleep(0);
break;
default:
break;
}
}
/// <summary>
/// Checks whether global strategy evaluation should be performed
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool ShouldEvaluateGlobalStrategy()
{
long now = Environment.TickCount64;
return now - Volatile.Read(ref s_lastGlobalEvaluationTicks) > EVALUATION_INTERVAL_MS;
}
/// <summary>
/// Determines whether strategy should be switched
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool ShouldSwitchStrategy()
{
// Fast convergence phase: reduce strategy switching
if (_localAttempts < FAST_CONVERGENCE_THRESHOLD && _consecutiveFailures < 32)
{
return false;
}
// Small probability exploration: occasionally try other strategies even when current is optimal
return NextRandom() % 100 < EXPLORATION_PROBABILITY || _currentMethod != s_globalBestMethod;
}
/// <summary>
/// Switches to the currently considered optimal strategy
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SwitchToOptimalStrategy()
{
BackoffMethod targetMethod;
// Most of the time choose the globally optimal
if (NextRandom() % 100 >= EXPLORATION_PROBABILITY)
{
targetMethod = s_globalBestMethod;
} else
{
// Small probability random exploration
targetMethod = (BackoffMethod)(NextRandom() % 3);
}
if (targetMethod != _currentMethod)
{
_currentMethod = targetMethod;
_spinWait = default; // Reset SpinWait state when switching strategies
_localAttempts = 0;
}
}
public enum BackoffMethod : byte
{
SpinWait = 0,
Yield = 1,
Sleep0 = 2
}
private struct PerformanceStats
{
public long Attempts;
public long LastResetTicks;
public long Successes;
public readonly double SuccessRate
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Attempts == 0 ? 0.0 : (double)Successes / Attempts;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RecordAttempt(bool success)
{
_ = Interlocked.Increment(ref Attempts);
if (success) {
_ = Interlocked.Increment(ref Successes);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset()
{
Attempts = 0;
Successes = 0;
LastResetTicks = Environment.TickCount64;
}
}
}
+125 -125
View File
@@ -1,125 +1,125 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="BufferExpansionLock"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.Threading
{
/// <summary>
/// Provide a lock for managing concurrent access to a buffer that can be expanded and use a
/// common two-pase-copy expanding mechanism.
/// </summary>
public class BufferExpansionLock : IDisposable
{
private static readonly ThreadLocal<int> Count = new(() => 0);
private readonly ReaderWriterLockSlim _copyLock;
private readonly ReaderWriterLockSlim _expansionLock;
public BufferExpansionLock()
{
_expansionLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
_copyLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
}
public bool IsThreadReadingMetaData
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _expansionLock.IsReadLockHeld;
}
public void Dispose()
{
_expansionLock?.Dispose();
_copyLock?.Dispose();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnterBufferCopy()
{
_copyLock.EnterWriteLock();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnterBufferRead()
{
int c = Count.Value;
if (c == 0) {
_copyLock.EnterReadLock();
}
Count.Value = c + 1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnterExpansionProcess()
{
_expansionLock.EnterWriteLock();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnterMetaDataRead()
{
_expansionLock.EnterReadLock();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExitBufferCopy()
{
_copyLock.ExitWriteLock();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExitBufferRead()
{
int c = Count.Value - 1;
Count.Value = c;
if (c == 0) {
_copyLock.ExitReadLock();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExitExpansionProcess()
{
_expansionLock.ExitWriteLock();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExitMetaDataRead()
{
_expansionLock.ExitReadLock();
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="BufferExpansionLock"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.Threading
{
/// <summary>
/// Provide a lock for managing concurrent access to a buffer that can be expanded and use a
/// common two-pase-copy expanding mechanism.
/// </summary>
public class BufferExpansionLock : IDisposable
{
private static readonly ThreadLocal<int> Count = new(() => 0);
private readonly ReaderWriterLockSlim _copyLock;
private readonly ReaderWriterLockSlim _expansionLock;
public BufferExpansionLock()
{
_expansionLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
_copyLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
}
public bool IsThreadReadingMetaData
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _expansionLock.IsReadLockHeld;
}
public void Dispose()
{
_expansionLock?.Dispose();
_copyLock?.Dispose();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnterBufferCopy()
{
_copyLock.EnterWriteLock();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnterBufferRead()
{
int c = Count.Value;
if (c == 0) {
_copyLock.EnterReadLock();
}
Count.Value = c + 1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnterExpansionProcess()
{
_expansionLock.EnterWriteLock();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnterMetaDataRead()
{
_expansionLock.EnterReadLock();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExitBufferCopy()
{
_copyLock.ExitWriteLock();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExitBufferRead()
{
int c = Count.Value - 1;
Count.Value = c;
if (c == 0) {
_copyLock.ExitReadLock();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExitExpansionProcess()
{
_expansionLock.ExitWriteLock();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExitMetaDataRead()
{
_expansionLock.ExitReadLock();
}
}
}
+154 -154
View File
@@ -1,154 +1,154 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="CancellableTask"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.Threading
{
public class CancellableTask : IDisposable
{
private bool disposedValue;
private CancellationTokenSource cts;
private protected Action<CancellationToken> _startup;
private protected Task _internalTask;
private protected CancellableTask()
{
cts = new CancellationTokenSource();
}
private protected CancellableTask(Action<CancellationToken> action)
{
_internalTask = new Task(InternalAction);
cts = new CancellationTokenSource();
}
public bool IsCompleted
{
get => _internalTask.IsCompleted;
}
public CancellationTokenSource Cts
{
get => cts;
}
public int Id
{
get => _internalTask.Id;
}
public void Cancel()
{
Cts.Cancel();
}
public void CancelAndWait()
{
Cts.Cancel();
_internalTask.Wait();
}
// ~CancellableTask()
// {
// Dispose(disposing: false);
// }
public void Dispose()
{
Dispose(disposing:true);
GC.SuppressFinalize(this);
}
public static CancellableTask Run(Action<CancellationToken> startup)
{
CancellableTask ret = new CancellableTask
{
_startup = startup
};
ret._internalTask = Task.Run(ret.InternalAction);
return ret;
}
public void Wait()
{
_internalTask.Wait();
}
public static void WaitAll(IEnumerable<CancellableTask> tasks)
{
IEnumerable<Task> convertedTasks = tasks.Select<CancellableTask, Task>(
ct => ct._internalTask);
Task.WaitAll(convertedTasks.ToArray());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void InternalAction()
{
_startup(Cts.Token);
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
if (IsCompleted)
{
cts.Dispose();
_internalTask.Dispose();
} else
{
throw new InvalidOperationException(
"Cannot dispose a cancellable task before completed");
}
}
disposedValue = true;
}
}
public static implicit operator Task(CancellableTask task)
{
return task._internalTask;
}
~CancellableTask()
{
Cts.Dispose();
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="CancellableTask"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.Threading
{
public class CancellableTask : IDisposable
{
private bool disposedValue;
private CancellationTokenSource cts;
private protected Action<CancellationToken> _startup;
private protected Task _internalTask;
private protected CancellableTask()
{
cts = new CancellationTokenSource();
}
private protected CancellableTask(Action<CancellationToken> action)
{
_internalTask = new Task(InternalAction);
cts = new CancellationTokenSource();
}
public bool IsCompleted
{
get => _internalTask.IsCompleted;
}
public CancellationTokenSource Cts
{
get => cts;
}
public int Id
{
get => _internalTask.Id;
}
public void Cancel()
{
Cts.Cancel();
}
public void CancelAndWait()
{
Cts.Cancel();
_internalTask.Wait();
}
// ~CancellableTask()
// {
// Dispose(disposing: false);
// }
public void Dispose()
{
Dispose(disposing:true);
GC.SuppressFinalize(this);
}
public static CancellableTask Run(Action<CancellationToken> startup)
{
CancellableTask ret = new CancellableTask
{
_startup = startup
};
ret._internalTask = Task.Run(ret.InternalAction);
return ret;
}
public void Wait()
{
_internalTask.Wait();
}
public static void WaitAll(IEnumerable<CancellableTask> tasks)
{
IEnumerable<Task> convertedTasks = tasks.Select<CancellableTask, Task>(
ct => ct._internalTask);
Task.WaitAll(convertedTasks.ToArray());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void InternalAction()
{
_startup(Cts.Token);
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
if (IsCompleted)
{
cts.Dispose();
_internalTask.Dispose();
} else
{
throw new InvalidOperationException(
"Cannot dispose a cancellable task before completed");
}
}
disposedValue = true;
}
}
public static implicit operator Task(CancellableTask task)
{
return task._internalTask;
}
~CancellableTask()
{
Cts.Dispose();
}
}
}
+58 -58
View File
@@ -1,58 +1,58 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="EmptyThread"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.Threading
{
public static partial class ThreadHelper
{
private static readonly Thread _emptyThreadCache = new Thread(() => { });
static ThreadHelper()
{
_emptyThreadCache.Start();
}
/// <summary>
/// An individual thread doing nothing, and its <see cref="Thread.IsAlive" /> <see
/// langword="property" /> always returns false.
/// </summary>
public static Thread EmptyThread
{
get => _emptyThreadCache;
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="EmptyThread"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.Threading
{
public static partial class ThreadHelper
{
private static readonly Thread _emptyThreadCache = new Thread(() => { });
static ThreadHelper()
{
_emptyThreadCache.Start();
}
/// <summary>
/// An individual thread doing nothing, and its <see cref="Thread.IsAlive" /> <see
/// langword="property" /> always returns false.
/// </summary>
public static Thread EmptyThread
{
get => _emptyThreadCache;
}
}
}
+51 -51
View File
@@ -1,51 +1,51 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="MainThreadAssert"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.Threading;
namespace StgSharp.Threading
{
public static partial class ThreadHelper
{
private static readonly int mainThreadId = Environment.CurrentManagedThreadId;
private static readonly ThreadLocal<bool> isNotMainThread = new(
() => Environment.CurrentManagedThreadId != mainThreadId);
[Conditional("DEBUG")]
internal static void MainThreadAssert()
{
if (isNotMainThread.Value) {
throw new InvalidOperationException(
$"Attempt to call sensitive operation which can be only called from main thread from thread {Environment.CurrentManagedThreadId}.");
}
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="MainThreadAssert"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.Threading;
namespace StgSharp.Threading
{
public static partial class ThreadHelper
{
private static readonly int mainThreadId = Environment.CurrentManagedThreadId;
private static readonly ThreadLocal<bool> isNotMainThread = new(
() => Environment.CurrentManagedThreadId != mainThreadId);
[Conditional("DEBUG")]
internal static void MainThreadAssert()
{
if (isNotMainThread.Value) {
throw new InvalidOperationException(
$"Attempt to call sensitive operation which can be only called from main thread from thread {Environment.CurrentManagedThreadId}.");
}
}
}
}
+47 -47
View File
@@ -1,47 +1,47 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="NumaBuffer"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Common.Threading
{
public unsafe struct NumaBuffer<T> where T : unmanaged
{
public readonly T* Buffers;
public readonly int BufferColumnCapacity;
public readonly int BufferRowCapacity;
public int CurrentColumnCount;
public int CurrentRowCount;
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="NumaBuffer"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Common.Threading
{
public unsafe struct NumaBuffer<T> where T : unmanaged
{
public readonly T* Buffers;
public readonly int BufferColumnCapacity;
public readonly int BufferRowCapacity;
public int CurrentColumnCount;
public int CurrentRowCount;
}
}
+174 -174
View File
@@ -1,175 +1,175 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SingleConsumerBell"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Threading;
namespace StgSharp.Threading
{
/// <summary>
/// A single-consumer bell/signal primitive.
/// </summary>
/// <remarks>
/// <para> Producers can call <see cref="Set" /> multiple times to publish signals. The bell
/// keeps a pending count of not-yet-consumed signals. The consumer uses <see
/// cref="Wait(object, CancellationToken)" /> or <see cref="TryWait(object)" /> to consume
/// signals.</para> <para> When a producer increments the pending count from 0 to 1, the
/// internal event is set to wake the consumer. Consuming via <see cref="TryWait(object)" />
/// or <see cref="Wait(object, CancellationToken)" /> clears the entire pending count
/// (aggregate consumption). If new signals arrive while resetting, the event is set again to
/// avoid missed signals.</para> <para> Ownership: only the specified <c> owner </c> can call
/// the consumer APIs.</para>
/// </remarks>
public sealed class SingleConsumerBell : IDisposable
{
private volatile bool _disposed;
private int _pending; // not volatile: used with Interlocked/Volatile APIs to avoid CS0420
private readonly ManualResetEventSlim _evt;
private readonly object _owner;
/// <summary>
/// Initializes a new instance of <see cref="SingleConsumerBell" />.
/// </summary>
/// <param name="owner">
/// The unique consumer identity; subsequent calls to Wait/TryWait must pass the same
/// reference.
/// </param>
/// <param name="spinCount">
/// Spin count passed to the underlying <see cref="ManualResetEventSlim" />.
/// </param>
/// <exception cref="ArgumentNullException">
/// Thrown if <paramref name="owner" /> is null.
/// </exception>
public SingleConsumerBell(object owner, int spinCount = 100)
{
_owner = owner ?? throw new ArgumentNullException(nameof(owner));
_evt = new ManualResetEventSlim(false, spinCount);
}
/// <summary>
/// Disposes the bell, sets the event to wake any waiting consumer, and releases
/// resources.
/// </summary>
public void Dispose()
{
_disposed = true;
_evt.Set();
_evt.Dispose();
}
/// <summary>
/// Publishes a signal by incrementing the not-yet-consumed count and returns the current
/// pending count.
/// </summary>
/// <remarks>
/// If the pending count transitions from 0 to 1, the event is set to wake the consumer.
/// </remarks>
/// <returns>
/// The current pending count including this call.
/// </returns>
public int Set()
{
if (_disposed) {
return Volatile.Read(ref _pending);
}
// Increment pending; if we transitioned 0 -> 1, set the event to wake the consumer
int newCount = Interlocked.Increment(ref _pending);
if (newCount == 1) {
_evt.Set();
}
return newCount;
}
/// <summary>
/// Attempts to consume any pending signals without blocking.
/// </summary>
/// <param name="consumer">
/// The consumer object; must be the same reference provided at construction.
/// </param>
/// <returns>
/// <c> true </c> if a signal was available (pending or event set); otherwise <c> false
/// </c>.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Thrown if <paramref name="consumer" /> is not the owner.
/// </exception>
public bool TryWait(object consumer)
{
if (!ReferenceEquals(_owner, consumer)) {
throw new InvalidOperationException("This bell is owned by another consumer.");
}
if (!_evt.IsSet && Volatile.Read(ref _pending) == 0) {
return false;
}
// Aggregate consumption: clear pending and reset event
_ = Interlocked.Exchange(ref _pending, 0);
_evt.Reset();
// If concurrent Set happened during reset, re-set the event to avoid lost signals
if (Volatile.Read(ref _pending) != 0)
{
_evt.Set();
}
return true;
}
/// <summary>
/// Blocks until a signal is available, then consumes all pending signals.
/// </summary>
/// <param name="consumer">
/// The consumer object; must be the same reference provided at construction.
/// </param>
/// <param name="cancellationToken">
/// A token to cancel the wait.
/// </param>
/// <exception cref="InvalidOperationException">
/// Thrown if <paramref name="consumer" /> is not the owner.
/// </exception>
public void Wait(object consumer, CancellationToken cancellationToken = default)
{
if (!ReferenceEquals(_owner, consumer)) {
throw new InvalidOperationException("This bell is owned by another consumer.");
}
_evt.Wait(cancellationToken);
// Consume this round of signals: clear pending and handle reset/concurrency
_ = Interlocked.Exchange(ref _pending, 0);
_evt.Reset();
if (Volatile.Read(ref _pending) != 0) {
_evt.Set();
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SingleConsumerBell"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Threading;
namespace StgSharp.Threading
{
/// <summary>
/// A single-consumer bell/signal primitive.
/// </summary>
/// <remarks>
/// <para> Producers can call <see cref="Set" /> multiple times to publish signals. The bell
/// keeps a pending count of not-yet-consumed signals. The consumer uses <see
/// cref="Wait(object, CancellationToken)" /> or <see cref="TryWait(object)" /> to consume
/// signals.</para> <para> When a producer increments the pending count from 0 to 1, the
/// internal event is set to wake the consumer. Consuming via <see cref="TryWait(object)" />
/// or <see cref="Wait(object, CancellationToken)" /> clears the entire pending count
/// (aggregate consumption). If new signals arrive while resetting, the event is set again to
/// avoid missed signals.</para> <para> Ownership: only the specified <c> owner </c> can call
/// the consumer APIs.</para>
/// </remarks>
public sealed class SingleConsumerBell : IDisposable
{
private volatile bool _disposed;
private int _pending; // not volatile: used with Interlocked/Volatile APIs to avoid CS0420
private readonly ManualResetEventSlim _evt;
private readonly object _owner;
/// <summary>
/// Initializes a new instance of <see cref="SingleConsumerBell" />.
/// </summary>
/// <param name="owner">
/// The unique consumer identity; subsequent calls to Wait/TryWait must pass the same
/// reference.
/// </param>
/// <param name="spinCount">
/// Spin count passed to the underlying <see cref="ManualResetEventSlim" />.
/// </param>
/// <exception cref="ArgumentNullException">
/// Thrown if <paramref name="owner" /> is null.
/// </exception>
public SingleConsumerBell(object owner, int spinCount = 100)
{
_owner = owner ?? throw new ArgumentNullException(nameof(owner));
_evt = new ManualResetEventSlim(false, spinCount);
}
/// <summary>
/// Disposes the bell, sets the event to wake any waiting consumer, and releases
/// resources.
/// </summary>
public void Dispose()
{
_disposed = true;
_evt.Set();
_evt.Dispose();
}
/// <summary>
/// Publishes a signal by incrementing the not-yet-consumed count and returns the current
/// pending count.
/// </summary>
/// <remarks>
/// If the pending count transitions from 0 to 1, the event is set to wake the consumer.
/// </remarks>
/// <returns>
/// The current pending count including this call.
/// </returns>
public int Set()
{
if (_disposed) {
return Volatile.Read(ref _pending);
}
// Increment pending; if we transitioned 0 -> 1, set the event to wake the consumer
int newCount = Interlocked.Increment(ref _pending);
if (newCount == 1) {
_evt.Set();
}
return newCount;
}
/// <summary>
/// Attempts to consume any pending signals without blocking.
/// </summary>
/// <param name="consumer">
/// The consumer object; must be the same reference provided at construction.
/// </param>
/// <returns>
/// <c> true </c> if a signal was available (pending or event set); otherwise <c> false
/// </c>.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Thrown if <paramref name="consumer" /> is not the owner.
/// </exception>
public bool TryWait(object consumer)
{
if (!ReferenceEquals(_owner, consumer)) {
throw new InvalidOperationException("This bell is owned by another consumer.");
}
if (!_evt.IsSet && Volatile.Read(ref _pending) == 0) {
return false;
}
// Aggregate consumption: clear pending and reset event
_ = Interlocked.Exchange(ref _pending, 0);
_evt.Reset();
// If concurrent Set happened during reset, re-set the event to avoid lost signals
if (Volatile.Read(ref _pending) != 0)
{
_evt.Set();
}
return true;
}
/// <summary>
/// Blocks until a signal is available, then consumes all pending signals.
/// </summary>
/// <param name="consumer">
/// The consumer object; must be the same reference provided at construction.
/// </param>
/// <param name="cancellationToken">
/// A token to cancel the wait.
/// </param>
/// <exception cref="InvalidOperationException">
/// Thrown if <paramref name="consumer" /> is not the owner.
/// </exception>
public void Wait(object consumer, CancellationToken cancellationToken = default)
{
if (!ReferenceEquals(_owner, consumer)) {
throw new InvalidOperationException("This bell is owned by another consumer.");
}
_evt.Wait(cancellationToken);
// Consume this round of signals: clear pending and handle reset/concurrency
_ = Interlocked.Exchange(ref _pending, 0);
_evt.Reset();
if (Volatile.Read(ref _pending) != 0) {
_evt.Set();
}
}
}
}
@@ -1,316 +1,316 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="VariableCapacitySemaphore"
// Project: StgSharp.Common
// AuthorGroup: Nitload
// License: MIT
// -----------------------------------------------------------------------
using System;
using System.Runtime.CompilerServices;
using System.Threading;
namespace StgSharp.Threading
{
/// <summary>
/// A counting semaphore with adjustable maximum capacity at runtime. Implemented using one or
/// more ManualResetEventSlim (MRES) gates and atomic counters to avoid kernel semaphore
/// overhead while supporting high-frequency Release/Wait patterns. Notes: - This is a
/// counting primitive (not a barrier). Permits accumulate up to <see cref="MaxPermits" />. -
/// Reducing <see cref="MaxPermits" /> does not revoke already issued permits; it only clamps
/// future releases. - Striped MRES can reduce contention in highly parallel waits.
/// </summary>
public sealed class VariableCapacitySemaphore : IDisposable
{
// MRES stripes (one gate per stripe). All stripes are set when transitioning 0 -> >0 and reset at >0 -> 0.
private readonly ManualResetEventSlim[] _events;
private volatile int _current;
private volatile int _max;
private readonly int _stripeMask; // for power-of-two stripe counts; otherwise -1
/// <summary>
/// Create a new variable-capacity semaphore.
/// </summary>
/// <param name="initialCount">
/// Initial available permits (clamped to [0, maxPermits]).
/// </param>
/// <param name="maxPermits">
/// Logical maximum permits (>= 1).
/// </param>
/// <param name="stripes">
/// Optional stripe count for MRES (<= 0 to auto choose by CPU).
/// </param>
public VariableCapacitySemaphore(int initialCount = 0, int maxPermits = 1, int stripes = 0)
{
ArgumentOutOfRangeException.ThrowIfLessThan(maxPermits, 1);
ArgumentOutOfRangeException.ThrowIfNegative(initialCount);
_max = maxPermits;
_current = Math.Min(initialCount, maxPermits);
int stripeCount = stripes > 0 ? stripes : ClampToPow2(Math.Min(Environment.ProcessorCount, 8));
_events = new ManualResetEventSlim[stripeCount];
bool set = _current > 0;
for (int i = 0; i < stripeCount; i++) {
_events[i] = new ManualResetEventSlim(set, 0);
}
_stripeMask = IsPow2(stripeCount) ? (stripeCount - 1) : -1;
}
/// <summary>
/// Current number of available permits.
/// </summary>
public int CurrentCount => Volatile.Read(ref _current);
/// <summary>
/// Logical maximum number of permits. Can be changed at runtime.
/// </summary>
public int MaxPermits
{
get => Volatile.Read(ref _max);
set
{
ArgumentOutOfRangeException.ThrowIfLessThan(value, 1);
Volatile.Write(ref _max, value);
// Clamp current to not exceed the new max (do not block; let Wait() consume down).
while (true)
{
int cur = Volatile.Read(ref _current);
if (cur <= value)
{
break;
}
if (Interlocked.CompareExchange(ref _current, value, cur) == cur)
{
break;
}
}
// Reflect availability to gates
if (Volatile.Read(ref _current) > 0)
{
SetAll();
} else
{
ResetAll();
}
}
}
public void Dispose()
{
foreach (ManualResetEventSlim e in _events) {
e.Dispose();
}
}
/// <summary>
/// Release a single permit if under <see cref="MaxPermits" />. Returns the previous
/// count.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Release()
{
while (true)
{
int cur = Volatile.Read(ref _current);
int max = Volatile.Read(ref _max);
if (cur >= max) {
return cur;
}
if (Interlocked.CompareExchange(ref _current, cur + 1, cur) == cur)
{
if (cur == 0) {
SetAll();
}
return cur;
}
}
}
/// <summary>
/// Release multiple permits (capped by <see cref="MaxPermits" />). Returns the previous
/// count.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Release(int permits)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(permits);
while (true)
{
int cur = Volatile.Read(ref _current);
int max = Volatile.Read(ref _max);
if (cur >= max) {
return cur;
}
long target = (long)cur + permits;
if (target > max) {
target = max;
}
if (Interlocked.CompareExchange(ref _current, (int)target, cur) == cur)
{
if (cur == 0) {
SetAll();
}
return cur;
}
}
}
/// <summary>
/// Try to take a permit without blocking. Returns true if acquired.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryWait()
{
while (true)
{
int cur = Volatile.Read(ref _current);
if (cur == 0) {
return false;
}
if (Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
{
if (cur - 1 == 0) {
ResetAll();
}
return true;
}
}
}
/// <summary>
/// Block until a permit is available, then consume one.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Wait()
{
ManualResetEventSlim gate = GetStripeEvent();
SpinWait spinner = new SpinWait();
while (true)
{
int cur = Volatile.Read(ref _current);
if (cur > 0 && Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
{
if (cur - 1 == 0) {
ResetAll();
}
return;
}
if (!spinner.NextSpinWillYield)
{
spinner.SpinOnce();
} else
{
gate.Wait();
}
}
}
/// <summary>
/// Block until a permit is available or canceled, then consume one.
/// </summary>
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken = default)
{
ManualResetEventSlim gate = GetStripeEvent();
SpinWait spinner = new SpinWait();
int start = Environment.TickCount;
int remaining = millisecondsTimeout;
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
int cur = Volatile.Read(ref _current);
if (cur > 0 && Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
{
if (cur - 1 == 0) {
ResetAll();
}
return true;
}
if (!spinner.NextSpinWillYield)
{
spinner.SpinOnce();
continue;
}
if (millisecondsTimeout < 0)
{
gate.Wait(cancellationToken);
continue;
}
if (remaining <= 0) {
return false;
}
if (!gate.Wait(remaining, cancellationToken)) {
return false;
}
remaining = millisecondsTimeout - (Environment.TickCount - start);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int ClampToPow2(int x)
{
if (x < 1) {
x = 1;
}
if (x > 8) {
x = 8;
}
int p = 1;
while ((p << 1) <= x) {
p <<= 1;
}
return p;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ManualResetEventSlim GetStripeEvent()
{
int id = Environment.CurrentManagedThreadId & 0x7fffffff;
if (_stripeMask != -1) {
return _events[id & _stripeMask];
}
int idx = id % _events.Length;
return _events[idx];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsPow2(int x) => (x & (x - 1)) == 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ResetAll()
{
foreach (ManualResetEventSlim e in _events) {
e.Reset();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetAll()
{
foreach (ManualResetEventSlim e in _events) {
e.Set();
}
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="VariableCapacitySemaphore"
// Project: StgSharp.Common
// AuthorGroup: Nitload
// License: MIT
// -----------------------------------------------------------------------
using System;
using System.Runtime.CompilerServices;
using System.Threading;
namespace StgSharp.Threading
{
/// <summary>
/// A counting semaphore with adjustable maximum capacity at runtime. Implemented using one or
/// more ManualResetEventSlim (MRES) gates and atomic counters to avoid kernel semaphore
/// overhead while supporting high-frequency Release/Wait patterns. Notes: - This is a
/// counting primitive (not a barrier). Permits accumulate up to <see cref="MaxPermits" />. -
/// Reducing <see cref="MaxPermits" /> does not revoke already issued permits; it only clamps
/// future releases. - Striped MRES can reduce contention in highly parallel waits.
/// </summary>
public sealed class VariableCapacitySemaphore : IDisposable
{
// MRES stripes (one gate per stripe). All stripes are set when transitioning 0 -> >0 and reset at >0 -> 0.
private readonly ManualResetEventSlim[] _events;
private volatile int _current;
private volatile int _max;
private readonly int _stripeMask; // for power-of-two stripe counts; otherwise -1
/// <summary>
/// Create a new variable-capacity semaphore.
/// </summary>
/// <param name="initialCount">
/// Initial available permits (clamped to [0, maxPermits]).
/// </param>
/// <param name="maxPermits">
/// Logical maximum permits (>= 1).
/// </param>
/// <param name="stripes">
/// Optional stripe count for MRES (<= 0 to auto choose by CPU).
/// </param>
public VariableCapacitySemaphore(int initialCount = 0, int maxPermits = 1, int stripes = 0)
{
ArgumentOutOfRangeException.ThrowIfLessThan(maxPermits, 1);
ArgumentOutOfRangeException.ThrowIfNegative(initialCount);
_max = maxPermits;
_current = Math.Min(initialCount, maxPermits);
int stripeCount = stripes > 0 ? stripes : ClampToPow2(Math.Min(Environment.ProcessorCount, 8));
_events = new ManualResetEventSlim[stripeCount];
bool set = _current > 0;
for (int i = 0; i < stripeCount; i++) {
_events[i] = new ManualResetEventSlim(set, 0);
}
_stripeMask = IsPow2(stripeCount) ? (stripeCount - 1) : -1;
}
/// <summary>
/// Current number of available permits.
/// </summary>
public int CurrentCount => Volatile.Read(ref _current);
/// <summary>
/// Logical maximum number of permits. Can be changed at runtime.
/// </summary>
public int MaxPermits
{
get => Volatile.Read(ref _max);
set
{
ArgumentOutOfRangeException.ThrowIfLessThan(value, 1);
Volatile.Write(ref _max, value);
// Clamp current to not exceed the new max (do not block; let Wait() consume down).
while (true)
{
int cur = Volatile.Read(ref _current);
if (cur <= value)
{
break;
}
if (Interlocked.CompareExchange(ref _current, value, cur) == cur)
{
break;
}
}
// Reflect availability to gates
if (Volatile.Read(ref _current) > 0)
{
SetAll();
} else
{
ResetAll();
}
}
}
public void Dispose()
{
foreach (ManualResetEventSlim e in _events) {
e.Dispose();
}
}
/// <summary>
/// Release a single permit if under <see cref="MaxPermits" />. Returns the previous
/// count.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Release()
{
while (true)
{
int cur = Volatile.Read(ref _current);
int max = Volatile.Read(ref _max);
if (cur >= max) {
return cur;
}
if (Interlocked.CompareExchange(ref _current, cur + 1, cur) == cur)
{
if (cur == 0) {
SetAll();
}
return cur;
}
}
}
/// <summary>
/// Release multiple permits (capped by <see cref="MaxPermits" />). Returns the previous
/// count.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Release(int permits)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(permits);
while (true)
{
int cur = Volatile.Read(ref _current);
int max = Volatile.Read(ref _max);
if (cur >= max) {
return cur;
}
long target = (long)cur + permits;
if (target > max) {
target = max;
}
if (Interlocked.CompareExchange(ref _current, (int)target, cur) == cur)
{
if (cur == 0) {
SetAll();
}
return cur;
}
}
}
/// <summary>
/// Try to take a permit without blocking. Returns true if acquired.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryWait()
{
while (true)
{
int cur = Volatile.Read(ref _current);
if (cur == 0) {
return false;
}
if (Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
{
if (cur - 1 == 0) {
ResetAll();
}
return true;
}
}
}
/// <summary>
/// Block until a permit is available, then consume one.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Wait()
{
ManualResetEventSlim gate = GetStripeEvent();
SpinWait spinner = new SpinWait();
while (true)
{
int cur = Volatile.Read(ref _current);
if (cur > 0 && Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
{
if (cur - 1 == 0) {
ResetAll();
}
return;
}
if (!spinner.NextSpinWillYield)
{
spinner.SpinOnce();
} else
{
gate.Wait();
}
}
}
/// <summary>
/// Block until a permit is available or canceled, then consume one.
/// </summary>
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken = default)
{
ManualResetEventSlim gate = GetStripeEvent();
SpinWait spinner = new SpinWait();
int start = Environment.TickCount;
int remaining = millisecondsTimeout;
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
int cur = Volatile.Read(ref _current);
if (cur > 0 && Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
{
if (cur - 1 == 0) {
ResetAll();
}
return true;
}
if (!spinner.NextSpinWillYield)
{
spinner.SpinOnce();
continue;
}
if (millisecondsTimeout < 0)
{
gate.Wait(cancellationToken);
continue;
}
if (remaining <= 0) {
return false;
}
if (!gate.Wait(remaining, cancellationToken)) {
return false;
}
remaining = millisecondsTimeout - (Environment.TickCount - start);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int ClampToPow2(int x)
{
if (x < 1) {
x = 1;
}
if (x > 8) {
x = 8;
}
int p = 1;
while ((p << 1) <= x) {
p <<= 1;
}
return p;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ManualResetEventSlim GetStripeEvent()
{
int id = Environment.CurrentManagedThreadId & 0x7fffffff;
if (_stripeMask != -1) {
return _events[id & _stripeMask];
}
int idx = id % _events.Length;
return _events[idx];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsPow2(int x) => (x & (x - 1)) == 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ResetAll()
{
foreach (ManualResetEventSlim e in _events) {
e.Reset();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetAll()
{
foreach (ManualResetEventSlim e in _events) {
e.Set();
}
}
}
}
+135 -135
View File
@@ -1,135 +1,135 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="StgSharpTime"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Collections;
using StgSharp.Timing;
using System;
using System.Diagnostics;
using System.Threading;
namespace StgSharp
{
public static partial class World
{
public static TimeSourceProviderBase MainTimeProvider => StgSharpTime.OnlyInstance;
}
public sealed class StgSharpTime : TimeSourceProviderBase
{
private static readonly LinearBag<TimeSpanProvider> _subscribers = new();
private static readonly Stopwatch mainProvider = new();
private static Thread timeProvideThread;
private StgSharpTime() { }
// Use Stopwatch frequency as base tick frequency (ticks per second).
public override long Frequency => Stopwatch.Frequency;
public static StgSharpTime OnlyInstance { get; } = new StgSharpTime();
public override long GetCurrentTimeSpanTick() => mainProvider.ElapsedTicks;
public sealed override void StartProvidingTime()
{
if (mainProvider.IsRunning) {
return;
}
mainProvider.Start();
timeProvideThread = new Thread(ProvideTime)
{
IsBackground = true,
Name = "StgSharpTime"
};
timeProvideThread.Start();
}
public override void StopProvidingTime()
{
lock (mainProvider) {
mainProvider.Stop();
}
}
public void Terminate()
{
lock (mainProvider) {
mainProvider.Stop();
}
}
protected sealed override void AddSubscriberUnsynced(TimeSpanProvider subscriber)
{
_subscribers.Add(subscriber);
}
protected sealed override void RemoveSubscriberUnsynced(TimeSpanProvider subscriber)
{
_subscribers.Remove(subscriber);
}
private void ProvideTime()
{
long internalFrequency = Stopwatch.Frequency; // ticks per second
lock (mainProvider) {
mainProvider.Restart();
}
while (mainProvider.IsRunning)
{
long elapsedTicks;
lock (mainProvider) {
elapsedTicks = mainProvider.ElapsedTicks;
}
if (_subscribers.Count == 0)
{
Thread.SpinWait(50);
continue;
}
lock (SubscribeLock)
{
for (int i = 0; i < _subscribers.Count; i++)
{
if (!_subscribers[i].OnSpanTick(elapsedTicks))
{
// Console.WriteLine("removed");
_subscribers.RemoveAt(i);
i--; // adjust index after removal
}
}
}
// Optional: brief spin to avoid tight loop if frame lengths are large.
Thread.SpinWait(20);
}
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="StgSharpTime"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Collections;
using StgSharp.Timing;
using System;
using System.Diagnostics;
using System.Threading;
namespace StgSharp
{
public static partial class World
{
public static TimeSourceProviderBase MainTimeProvider => StgSharpTime.OnlyInstance;
}
public sealed class StgSharpTime : TimeSourceProviderBase
{
private static readonly LinearBag<TimeSpanProvider> _subscribers = new();
private static readonly Stopwatch mainProvider = new();
private static Thread timeProvideThread;
private StgSharpTime() { }
// Use Stopwatch frequency as base tick frequency (ticks per second).
public override long Frequency => Stopwatch.Frequency;
public static StgSharpTime OnlyInstance { get; } = new StgSharpTime();
public override long GetCurrentTimeSpanTick() => mainProvider.ElapsedTicks;
public sealed override void StartProvidingTime()
{
if (mainProvider.IsRunning) {
return;
}
mainProvider.Start();
timeProvideThread = new Thread(ProvideTime)
{
IsBackground = true,
Name = "StgSharpTime"
};
timeProvideThread.Start();
}
public override void StopProvidingTime()
{
lock (mainProvider) {
mainProvider.Stop();
}
}
public void Terminate()
{
lock (mainProvider) {
mainProvider.Stop();
}
}
protected sealed override void AddSubscriberUnsynced(TimeSpanProvider subscriber)
{
_subscribers.Add(subscriber);
}
protected sealed override void RemoveSubscriberUnsynced(TimeSpanProvider subscriber)
{
_subscribers.Remove(subscriber);
}
private void ProvideTime()
{
long internalFrequency = Stopwatch.Frequency; // ticks per second
lock (mainProvider) {
mainProvider.Restart();
}
while (mainProvider.IsRunning)
{
long elapsedTicks;
lock (mainProvider) {
elapsedTicks = mainProvider.ElapsedTicks;
}
if (_subscribers.Count == 0)
{
Thread.SpinWait(50);
continue;
}
lock (SubscribeLock)
{
for (int i = 0; i < _subscribers.Count; i++)
{
if (!_subscribers[i].OnSpanTick(elapsedTicks))
{
// Console.WriteLine("removed");
_subscribers.RemoveAt(i);
i--; // adjust index after removal
}
}
}
// Optional: brief spin to avoid tight loop if frame lengths are large.
Thread.SpinWait(20);
}
}
}
}
+309 -309
View File
@@ -1,309 +1,309 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="TimeSequence"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Timing;
using System;
using System.Runtime.CompilerServices;
namespace StgSharp.Common.Timing
{
public abstract class TimeSequence
{
public long BeginningTick { get; protected set; }
public long EndingTick { get; protected set; }
public long PreviousFrameTick { get; protected set; }
public static TimeSequence CreateLinear(
double totalSeconds,
double frameSeconds
)
{
if (double.IsPositiveInfinity(totalSeconds)) {
return new InfiniteLinearTimeSequence(frameSeconds);
}
return new LinearTimeSequence(totalSeconds, frameSeconds);
}
public static TimeSequence CreateLinearInfinite(
double frameSeconds
)
{
return new InfiniteLinearTimeSequence(frameSeconds);
}
public static TimeSequence CreateLogarithmic(
double initialSliceSeconds,
double totalSeconds,
double growthMultiplier,
double minSliceSeconds
) => new LogTimeSequence(initialSliceSeconds, totalSeconds, growthMultiplier,
minSliceSeconds);
public abstract bool EndsAt(
long currentTick
);
public abstract bool IsNextFrameReady(
long currentTick
);
public abstract void StartFrom(
TimeSourceProviderBase source
);
}
internal sealed class LinearTimeSequence : TimeSequence
{
private readonly double _frameSeconds;
private readonly double _totalSeconds;
private long _frameLengthTicks;
public LinearTimeSequence(
double totalSeconds,
double frameSeconds
)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(totalSeconds);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(frameSeconds);
if (frameSeconds > totalSeconds) {
throw new ArgumentException("Frame length greater than total length.");
}
_totalSeconds = totalSeconds;
_frameSeconds = frameSeconds;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool EndsAt(
long currentTick
) => currentTick >= EndingTick;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool IsNextFrameReady(
long currentTick
)
{
long span = currentTick - PreviousFrameTick;
if (span >= _frameLengthTicks && currentTick < EndingTick)
{
PreviousFrameTick += _frameLengthTicks;
return true;
}
return false;
}
public override void StartFrom(
TimeSourceProviderBase source
)
{
long f = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
_frameLengthTicks = (long)Math.Max(1, _frameSeconds * f);
long totalTicks = (long)Math.Max(_frameLengthTicks, Math.Round(_totalSeconds * f, MidpointRounding.AwayFromZero));
BeginningTick = source.GetCurrentTimeSpanTick();
EndingTick = BeginningTick + totalTicks;
EndingTick = EndingTick < 0 ? long.MaxValue : EndingTick;
PreviousFrameTick = BeginningTick;
}
}
/// <summary>
/// Logarithmic time sequence with growth factor and minimum slice length enforcement.
/// </summary>
internal sealed class LogTimeSequence : TimeSequence
{
private bool _finalEmitted;
private readonly double _initialSeconds;
private double _lastBoundarySecondsAdjusted;
private readonly double _minSliceSeconds;
private readonly double _multiplier;
private double _nextBoundarySecondsAdjusted;
private readonly double _totalSeconds;
private int _sliceIndex;
private long _freq;
private long _nextBoundaryTick;
private long _totalTicks;
public LogTimeSequence(
double initialSliceSeconds,
double totalSeconds,
double growthMultiplier,
double minSliceSeconds
)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(initialSliceSeconds);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(totalSeconds);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(growthMultiplier);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(minSliceSeconds);
if (growthMultiplier < 1.0) {
throw new ArgumentException("growthMultiplier must be >= 1");
}
if (initialSliceSeconds > totalSeconds) {
throw new ArgumentException("Initial slice length greater than total length.");
}
_initialSeconds = initialSliceSeconds;
_totalSeconds = totalSeconds;
_multiplier = growthMultiplier;
_minSliceSeconds = minSliceSeconds;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool EndsAt(
long currentTick
)
{
return currentTick >= EndingTick;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool IsNextFrameReady(
long currentTick
)
{
// Emit final once when reaching or passing end.
if (currentTick >= EndingTick)
{
if (_finalEmitted) {
return false;
}
PreviousFrameTick = EndingTick;
_finalEmitted = true;
return true;
}
// Not reached end: only when we cross the next boundary tick do we emit.
if (currentTick < _nextBoundaryTick)
{
return false;
}
// Advance to this boundary.
PreviousFrameTick = _nextBoundaryTick;
_lastBoundarySecondsAdjusted = _nextBoundarySecondsAdjusted;
_sliceIndex++;
// Compute next cumulative boundary seconds with multiplier, then enforce min-slice and total cap.
double next = _initialSeconds * Math.Pow(_multiplier, _sliceIndex);
if (next > _totalSeconds) {
next = _totalSeconds;
}
double delta = next - _lastBoundarySecondsAdjusted;
if (delta < _minSliceSeconds)
{
next = _lastBoundarySecondsAdjusted + _minSliceSeconds;
if (next > _totalSeconds) {
next = _totalSeconds;
}
}
_nextBoundarySecondsAdjusted = next;
_nextBoundaryTick = (_nextBoundarySecondsAdjusted >= _totalSeconds) ?
EndingTick :
BeginningTick + SecondsToTicks(_nextBoundarySecondsAdjusted);
return true;
}
public override void StartFrom(
TimeSourceProviderBase source
)
{
_freq = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
BeginningTick = source.GetCurrentTimeSpanTick();
_totalTicks = SecondsToTicks(_totalSeconds);
EndingTick = BeginningTick + _totalTicks;
_sliceIndex = 0;
_lastBoundarySecondsAdjusted = 0.0;
_nextBoundarySecondsAdjusted = _initialSeconds;
_nextBoundaryTick = BeginningTick + SecondsToTicks(_nextBoundarySecondsAdjusted);
PreviousFrameTick = BeginningTick;
_finalEmitted = false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private long SecondsToTicks(
double seconds
) => (long)Math.Max(1L, Math.Round(seconds * _freq, MidpointRounding.AwayFromZero));
}
/// <summary>
/// Infinite linear time sequence: emits frames at fixed interval and never ends.
/// </summary>
internal sealed class InfiniteLinearTimeSequence : TimeSequence
{
private readonly double _frameSeconds;
private long _frameLengthTicks;
public InfiniteLinearTimeSequence(
double frameSeconds
)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(frameSeconds);
_frameSeconds = frameSeconds;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool EndsAt(
long currentTick
) => false; // never ends
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool IsNextFrameReady(
long currentTick
)
{
long span = currentTick - PreviousFrameTick;
if (span >= _frameLengthTicks)
{
PreviousFrameTick += _frameLengthTicks;
return true;
}
return false;
}
public override void StartFrom(
TimeSourceProviderBase source
)
{
long f = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
_frameLengthTicks = (long)Math.Max(1, _frameSeconds * f);
BeginningTick = source.GetCurrentTimeSpanTick();
EndingTick = long.MaxValue; // sentinel
PreviousFrameTick = BeginningTick;
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="TimeSequence"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Timing;
using System;
using System.Runtime.CompilerServices;
namespace StgSharp.Common.Timing
{
public abstract class TimeSequence
{
public long BeginningTick { get; protected set; }
public long EndingTick { get; protected set; }
public long PreviousFrameTick { get; protected set; }
public static TimeSequence CreateLinear(
double totalSeconds,
double frameSeconds
)
{
if (double.IsPositiveInfinity(totalSeconds)) {
return new InfiniteLinearTimeSequence(frameSeconds);
}
return new LinearTimeSequence(totalSeconds, frameSeconds);
}
public static TimeSequence CreateLinearInfinite(
double frameSeconds
)
{
return new InfiniteLinearTimeSequence(frameSeconds);
}
public static TimeSequence CreateLogarithmic(
double initialSliceSeconds,
double totalSeconds,
double growthMultiplier,
double minSliceSeconds
) => new LogTimeSequence(initialSliceSeconds, totalSeconds, growthMultiplier,
minSliceSeconds);
public abstract bool EndsAt(
long currentTick
);
public abstract bool IsNextFrameReady(
long currentTick
);
public abstract void StartFrom(
TimeSourceProviderBase source
);
}
internal sealed class LinearTimeSequence : TimeSequence
{
private readonly double _frameSeconds;
private readonly double _totalSeconds;
private long _frameLengthTicks;
public LinearTimeSequence(
double totalSeconds,
double frameSeconds
)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(totalSeconds);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(frameSeconds);
if (frameSeconds > totalSeconds) {
throw new ArgumentException("Frame length greater than total length.");
}
_totalSeconds = totalSeconds;
_frameSeconds = frameSeconds;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool EndsAt(
long currentTick
) => currentTick >= EndingTick;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool IsNextFrameReady(
long currentTick
)
{
long span = currentTick - PreviousFrameTick;
if (span >= _frameLengthTicks && currentTick < EndingTick)
{
PreviousFrameTick += _frameLengthTicks;
return true;
}
return false;
}
public override void StartFrom(
TimeSourceProviderBase source
)
{
long f = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
_frameLengthTicks = (long)Math.Max(1, _frameSeconds * f);
long totalTicks = (long)Math.Max(_frameLengthTicks, Math.Round(_totalSeconds * f, MidpointRounding.AwayFromZero));
BeginningTick = source.GetCurrentTimeSpanTick();
EndingTick = BeginningTick + totalTicks;
EndingTick = EndingTick < 0 ? long.MaxValue : EndingTick;
PreviousFrameTick = BeginningTick;
}
}
/// <summary>
/// Logarithmic time sequence with growth factor and minimum slice length enforcement.
/// </summary>
internal sealed class LogTimeSequence : TimeSequence
{
private bool _finalEmitted;
private readonly double _initialSeconds;
private double _lastBoundarySecondsAdjusted;
private readonly double _minSliceSeconds;
private readonly double _multiplier;
private double _nextBoundarySecondsAdjusted;
private readonly double _totalSeconds;
private int _sliceIndex;
private long _freq;
private long _nextBoundaryTick;
private long _totalTicks;
public LogTimeSequence(
double initialSliceSeconds,
double totalSeconds,
double growthMultiplier,
double minSliceSeconds
)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(initialSliceSeconds);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(totalSeconds);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(growthMultiplier);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(minSliceSeconds);
if (growthMultiplier < 1.0) {
throw new ArgumentException("growthMultiplier must be >= 1");
}
if (initialSliceSeconds > totalSeconds) {
throw new ArgumentException("Initial slice length greater than total length.");
}
_initialSeconds = initialSliceSeconds;
_totalSeconds = totalSeconds;
_multiplier = growthMultiplier;
_minSliceSeconds = minSliceSeconds;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool EndsAt(
long currentTick
)
{
return currentTick >= EndingTick;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool IsNextFrameReady(
long currentTick
)
{
// Emit final once when reaching or passing end.
if (currentTick >= EndingTick)
{
if (_finalEmitted) {
return false;
}
PreviousFrameTick = EndingTick;
_finalEmitted = true;
return true;
}
// Not reached end: only when we cross the next boundary tick do we emit.
if (currentTick < _nextBoundaryTick)
{
return false;
}
// Advance to this boundary.
PreviousFrameTick = _nextBoundaryTick;
_lastBoundarySecondsAdjusted = _nextBoundarySecondsAdjusted;
_sliceIndex++;
// Compute next cumulative boundary seconds with multiplier, then enforce min-slice and total cap.
double next = _initialSeconds * Math.Pow(_multiplier, _sliceIndex);
if (next > _totalSeconds) {
next = _totalSeconds;
}
double delta = next - _lastBoundarySecondsAdjusted;
if (delta < _minSliceSeconds)
{
next = _lastBoundarySecondsAdjusted + _minSliceSeconds;
if (next > _totalSeconds) {
next = _totalSeconds;
}
}
_nextBoundarySecondsAdjusted = next;
_nextBoundaryTick = (_nextBoundarySecondsAdjusted >= _totalSeconds) ?
EndingTick :
BeginningTick + SecondsToTicks(_nextBoundarySecondsAdjusted);
return true;
}
public override void StartFrom(
TimeSourceProviderBase source
)
{
_freq = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
BeginningTick = source.GetCurrentTimeSpanTick();
_totalTicks = SecondsToTicks(_totalSeconds);
EndingTick = BeginningTick + _totalTicks;
_sliceIndex = 0;
_lastBoundarySecondsAdjusted = 0.0;
_nextBoundarySecondsAdjusted = _initialSeconds;
_nextBoundaryTick = BeginningTick + SecondsToTicks(_nextBoundarySecondsAdjusted);
PreviousFrameTick = BeginningTick;
_finalEmitted = false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private long SecondsToTicks(
double seconds
) => (long)Math.Max(1L, Math.Round(seconds * _freq, MidpointRounding.AwayFromZero));
}
/// <summary>
/// Infinite linear time sequence: emits frames at fixed interval and never ends.
/// </summary>
internal sealed class InfiniteLinearTimeSequence : TimeSequence
{
private readonly double _frameSeconds;
private long _frameLengthTicks;
public InfiniteLinearTimeSequence(
double frameSeconds
)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(frameSeconds);
_frameSeconds = frameSeconds;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool EndsAt(
long currentTick
) => false; // never ends
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool IsNextFrameReady(
long currentTick
)
{
long span = currentTick - PreviousFrameTick;
if (span >= _frameLengthTicks)
{
PreviousFrameTick += _frameLengthTicks;
return true;
}
return false;
}
public override void StartFrom(
TimeSourceProviderBase source
)
{
long f = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
_frameLengthTicks = (long)Math.Max(1, _frameSeconds * f);
BeginningTick = source.GetCurrentTimeSpanTick();
EndingTick = long.MaxValue; // sentinel
PreviousFrameTick = BeginningTick;
}
}
}
+68 -68
View File
@@ -1,68 +1,68 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="TimeSourceProvider"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp;
using System;
using System.Linq;
using System.Threading;
namespace StgSharp.Timing
{
public abstract class TimeSourceProviderBase
{
public abstract long Frequency { get; }
protected object SubscribeLock { get; } = new();
public void AddSubscriber(TimeSpanProvider subscriber)
{
lock (SubscribeLock) {
AddSubscriberUnsynced(subscriber);
}
}
public abstract long GetCurrentTimeSpanTick();
public void RemoveSubscriber(TimeSpanProvider subscriber)
{
lock (SubscribeLock) {
RemoveSubscriberUnsynced(subscriber);
}
}
public abstract void StartProvidingTime();
public abstract void StopProvidingTime();
protected abstract void AddSubscriberUnsynced(TimeSpanProvider subscriber);
protected abstract void RemoveSubscriberUnsynced(TimeSpanProvider subscriber);
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="TimeSourceProvider"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp;
using System;
using System.Linq;
using System.Threading;
namespace StgSharp.Timing
{
public abstract class TimeSourceProviderBase
{
public abstract long Frequency { get; }
protected object SubscribeLock { get; } = new();
public void AddSubscriber(TimeSpanProvider subscriber)
{
lock (SubscribeLock) {
AddSubscriberUnsynced(subscriber);
}
}
public abstract long GetCurrentTimeSpanTick();
public void RemoveSubscriber(TimeSpanProvider subscriber)
{
lock (SubscribeLock) {
RemoveSubscriberUnsynced(subscriber);
}
}
public abstract void StartProvidingTime();
public abstract void StopProvidingTime();
protected abstract void AddSubscriberUnsynced(TimeSpanProvider subscriber);
protected abstract void RemoveSubscriberUnsynced(TimeSpanProvider subscriber);
}
}
@@ -1,234 +1,234 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="TimeSpanProvider.Awaiting"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Mathematics.Memory;
using System;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.Timing
{
public partial class TimeSpanProvider
{
private static readonly ConcurrentBufferStack<int> s_unusedID = new ConcurrentBufferStack<int>(8);
private static int s_maxID;
private EventHandler? _refreshHandler, _missHandler;
private int _size, _id;
private long _pendingSpans; // Number of unconsumed span ticks since last Wait.
private readonly ManualResetEventSlim _event = new();
private readonly object _participantObject = new();
public event EventHandler MissTimeSpanRefreshed
{
add => _missHandler += value;
remove => _missHandler -= value;
}
public event EventHandler TimeSpanRefreshed
{
add => _refreshHandler += value;
remove => _refreshHandler -= value;
}
/// <summary>
/// True if there is at least one unconsumed span tick (PendingSpanCount &gt; 0).
/// </summary>
public bool HasPendingSpan => PendingSpanCount > 0;
public int TokenID
{
get => _id;
private set => _id = value;
}
/// <summary>
/// Number of span ticks not yet consumed by Wait ( &gt; 0 means last tick not waited).
/// </summary>
public long PendingSpanCount => Volatile.Read(ref _pendingSpans);
internal ManualResetEventSlim AwaitingEvent
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _event;
}
public void Dispose()
{
// mark provider as ended so external users can observe terminal state
Volatile.Write(ref _endedFlag, 1);
s_unusedID.Push(TokenID);
_event.Set();
GC.SuppressFinalize(this);
}
public override int GetHashCode() => _id;
public void Participant()
{
lock (_participantObject)
{
// Increase the maximum concurrent consumers for the next tick.
_size++;
}
}
public void WaitNextSpan()
{
_event.Wait();
// Consume one span slot (clamp at 0 so multiple subscribers won't make it negative).
while (true)
{
long cur = Volatile.Read(ref _pendingSpans);
if (cur == 0) {
return;
}
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
{
if (cur - 1 == 0)
{
// Last consumer resets the gate for the next tick.
_event.Reset();
}
return;
}
}
}
public async Task WaitNextSpanAsync()
{
await Task.Run(() => _event.Wait()).ConfigureAwait(false);
while (true)
{
long cur = Volatile.Read(ref _pendingSpans);
if (cur == 0)
{
break;
}
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
{
if (cur - 1 == 0) {
_event.Reset();
}
break;
}
}
}
/// <summary>
/// Wait for the next timespan tick, but honor the provided cancellation token. Throws
/// OperationCanceledException when token is cancelled. If the provider is disposed during
/// wait an OperationCanceledException is thrown to indicate end.
/// </summary>
public Task WaitNextSpanAsync(CancellationToken ct)
{
// Block the current thread until the event is signaled or cancellation requested.
_event.Wait(ct);
// consume one pending span (same logic as non-cancellable variant)
while (true)
{
long cur = Volatile.Read(ref _pendingSpans);
if (cur == 0)
{
break;
}
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
{
if (cur - 1 == 0) {
_event.Reset();
}
break;
}
}
return Task.CompletedTask;
}
internal void MissRefresh() => _missHandler?.Invoke(this, EventArgs.Empty); // Legacy compatibility.
/// <summary>
///
/// </summary>
/// <param name="currentTick">
/// Tick passed since binded time source.
/// </param>
/// <returns>
/// True if the span provider is working. False if time is over (the sequence ended).
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool OnSpanTick(long currentTick)
{
if (_sequence.EndsAt(currentTick))
{
// mark ended due to sequence timeout, then dispose
Volatile.Write(ref _endedFlag, 1);
// Console.WriteLine("Time span ended.");
Dispose();
return false;
}
if (!_sequence.IsNextFrameReady(currentTick)) {
return true;
}
// Console.WriteLine("Frame hit!");
// Fast path: if no pending consumers, initialize the counter to current capacity (_size).
if (Volatile.Read(ref _pendingSpans) == 0)
{
int toIssue = Math.Max(1, Volatile.Read(ref _size));
if (Interlocked.CompareExchange(ref _pendingSpans, toIssue, 0) == 0)
{
// Open the gate for all current waiters (up to `_size`).
_event.Set();
_refreshHandler?.Invoke(this, EventArgs.Empty);
}
} else
{
// A tick is already pending (or was just set by another thread): mark as miss.
_missHandler?.Invoke(this, EventArgs.Empty);
}
return true;
}
~TimeSpanProvider() => Dispose();
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="TimeSpanProvider.Awaiting"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Mathematics.Memory;
using System;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.Timing
{
public partial class TimeSpanProvider
{
private static readonly ConcurrentBufferStack<int> s_unusedID = new ConcurrentBufferStack<int>(8);
private static int s_maxID;
private EventHandler? _refreshHandler, _missHandler;
private int _size, _id;
private long _pendingSpans; // Number of unconsumed span ticks since last Wait.
private readonly ManualResetEventSlim _event = new();
private readonly object _participantObject = new();
public event EventHandler MissTimeSpanRefreshed
{
add => _missHandler += value;
remove => _missHandler -= value;
}
public event EventHandler TimeSpanRefreshed
{
add => _refreshHandler += value;
remove => _refreshHandler -= value;
}
/// <summary>
/// True if there is at least one unconsumed span tick (PendingSpanCount &gt; 0).
/// </summary>
public bool HasPendingSpan => PendingSpanCount > 0;
public int TokenID
{
get => _id;
private set => _id = value;
}
/// <summary>
/// Number of span ticks not yet consumed by Wait ( &gt; 0 means last tick not waited).
/// </summary>
public long PendingSpanCount => Volatile.Read(ref _pendingSpans);
internal ManualResetEventSlim AwaitingEvent
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _event;
}
public void Dispose()
{
// mark provider as ended so external users can observe terminal state
Volatile.Write(ref _endedFlag, 1);
s_unusedID.Push(TokenID);
_event.Set();
GC.SuppressFinalize(this);
}
public override int GetHashCode() => _id;
public void Participant()
{
lock (_participantObject)
{
// Increase the maximum concurrent consumers for the next tick.
_size++;
}
}
public void WaitNextSpan()
{
_event.Wait();
// Consume one span slot (clamp at 0 so multiple subscribers won't make it negative).
while (true)
{
long cur = Volatile.Read(ref _pendingSpans);
if (cur == 0) {
return;
}
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
{
if (cur - 1 == 0)
{
// Last consumer resets the gate for the next tick.
_event.Reset();
}
return;
}
}
}
public async Task WaitNextSpanAsync()
{
await Task.Run(() => _event.Wait()).ConfigureAwait(false);
while (true)
{
long cur = Volatile.Read(ref _pendingSpans);
if (cur == 0)
{
break;
}
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
{
if (cur - 1 == 0) {
_event.Reset();
}
break;
}
}
}
/// <summary>
/// Wait for the next timespan tick, but honor the provided cancellation token. Throws
/// OperationCanceledException when token is cancelled. If the provider is disposed during
/// wait an OperationCanceledException is thrown to indicate end.
/// </summary>
public Task WaitNextSpanAsync(CancellationToken ct)
{
// Block the current thread until the event is signaled or cancellation requested.
_event.Wait(ct);
// consume one pending span (same logic as non-cancellable variant)
while (true)
{
long cur = Volatile.Read(ref _pendingSpans);
if (cur == 0)
{
break;
}
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
{
if (cur - 1 == 0) {
_event.Reset();
}
break;
}
}
return Task.CompletedTask;
}
internal void MissRefresh() => _missHandler?.Invoke(this, EventArgs.Empty); // Legacy compatibility.
/// <summary>
///
/// </summary>
/// <param name="currentTick">
/// Tick passed since binded time source.
/// </param>
/// <returns>
/// True if the span provider is working. False if time is over (the sequence ended).
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool OnSpanTick(long currentTick)
{
if (_sequence.EndsAt(currentTick))
{
// mark ended due to sequence timeout, then dispose
Volatile.Write(ref _endedFlag, 1);
// Console.WriteLine("Time span ended.");
Dispose();
return false;
}
if (!_sequence.IsNextFrameReady(currentTick)) {
return true;
}
// Console.WriteLine("Frame hit!");
// Fast path: if no pending consumers, initialize the counter to current capacity (_size).
if (Volatile.Read(ref _pendingSpans) == 0)
{
int toIssue = Math.Max(1, Volatile.Read(ref _size));
if (Interlocked.CompareExchange(ref _pendingSpans, toIssue, 0) == 0)
{
// Open the gate for all current waiters (up to `_size`).
_event.Set();
_refreshHandler?.Invoke(this, EventArgs.Empty);
}
} else
{
// A tick is already pending (or was just set by another thread): mark as miss.
_missHandler?.Invoke(this, EventArgs.Empty);
}
return true;
}
~TimeSpanProvider() => Dispose();
}
}
+93 -93
View File
@@ -1,94 +1,94 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="TimeSpanProvider"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Common.Timing;
using System;
using System.Runtime.CompilerServices;
using System.Threading;
namespace StgSharp.Timing
{
/// <summary>
/// TimeSpanProvider: uses a bound TimeSequence and TimeSourceProviderBase to publish span
/// ticks. Each OnSpanTick advances the sequence; when sequence ends provider unsubscribes.
/// </summary>
public partial class TimeSpanProvider
{
// private bool _subscribed;
private int _emittedSpanCount; // legacy counter
// internal ended flag (0 = running, 1 = ended)
private int _endedFlag;
private readonly TimeSequence _sequence;
private readonly TimeSourceProviderBase _timeSource;
public TimeSpanProvider(
TimeSequence sequence,
TimeSourceProviderBase provider,
int maxWaitCount = 1)
{
ArgumentNullException.ThrowIfNull(provider);
ArgumentNullException.ThrowIfNull(sequence);
_timeSource = provider;
_sequence = sequence;
_sequence.StartFrom(provider);
provider.AddSubscriber(this);
// _subscribed = true;
if (!s_unusedID.TryPop(out int id))
{
id = Interlocked.Increment(ref s_maxID);
}
TokenID = id;
_size = maxWaitCount > 0 ? maxWaitCount : 1;
_event = new ManualResetEventSlim(initialState:false, spinCount:0);
_pendingSpans = 0;
}
/// <summary>
/// True when the provider has reached its end and will not emit further spans.
/// </summary>
public bool IsEnded => Volatile.Read(ref _endedFlag) != 0;
/// <summary>
/// Current emitted time in seconds relative to the sequence beginning.
/// </summary>
public double CurrentSecond
{
get => (double)(_sequence.PreviousFrameTick - _sequence.BeginningTick) / _timeSource.Frequency;
}
public int CurrentSpan => _emittedSpanCount;
public static TimeSourceProviderBase DefaultProvider => World.MainTimeProvider;
public static explicit operator int(TimeSpanProvider provider) => provider?._emittedSpanCount ??
0;
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="TimeSpanProvider"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Common.Timing;
using System;
using System.Runtime.CompilerServices;
using System.Threading;
namespace StgSharp.Timing
{
/// <summary>
/// TimeSpanProvider: uses a bound TimeSequence and TimeSourceProviderBase to publish span
/// ticks. Each OnSpanTick advances the sequence; when sequence ends provider unsubscribes.
/// </summary>
public partial class TimeSpanProvider
{
// private bool _subscribed;
private int _emittedSpanCount; // legacy counter
// internal ended flag (0 = running, 1 = ended)
private int _endedFlag;
private readonly TimeSequence _sequence;
private readonly TimeSourceProviderBase _timeSource;
public TimeSpanProvider(
TimeSequence sequence,
TimeSourceProviderBase provider,
int maxWaitCount = 1)
{
ArgumentNullException.ThrowIfNull(provider);
ArgumentNullException.ThrowIfNull(sequence);
_timeSource = provider;
_sequence = sequence;
_sequence.StartFrom(provider);
provider.AddSubscriber(this);
// _subscribed = true;
if (!s_unusedID.TryPop(out int id))
{
id = Interlocked.Increment(ref s_maxID);
}
TokenID = id;
_size = maxWaitCount > 0 ? maxWaitCount : 1;
_event = new ManualResetEventSlim(initialState:false, spinCount:0);
_pendingSpans = 0;
}
/// <summary>
/// True when the provider has reached its end and will not emit further spans.
/// </summary>
public bool IsEnded => Volatile.Read(ref _endedFlag) != 0;
/// <summary>
/// Current emitted time in seconds relative to the sequence beginning.
/// </summary>
public double CurrentSecond
{
get => (double)(_sequence.PreviousFrameTick - _sequence.BeginningTick) / _timeSource.Frequency;
}
public int CurrentSpan => _emittedSpanCount;
public static TimeSourceProviderBase DefaultProvider => World.MainTimeProvider;
public static explicit operator int(TimeSpanProvider provider) => provider?._emittedSpanCount ??
0;
}
}
+8 -8
View File
@@ -1,8 +1,8 @@
namespace EM.LowLevel
{
/// <summary>
/// Marker interface for command types that can be built into bytes by an ICommandBuilder.
/// Implement this on record/struct command shapes.
/// </summary>
public interface ICommand { }
}
namespace EM.LowLevel
{
/// <summary>
/// Marker interface for command types that can be built into bytes by an ICommandBuilder.
/// Implement this on record/struct command shapes.
/// </summary>
public interface ICommand { }
}
+30 -30
View File
@@ -1,30 +1,30 @@
namespace EM.LowLevel
{
/// <summary>
/// Builds device/peripheral commands from C#-friendly types (enums, records, structs, etc.).
/// </summary>
public interface ICommandBuilder
{
/// <summary>
/// Build a command payload (already framed or raw depending on implementation contract) from a
/// strongly-typed command description.
/// </summary>
/// <typeparam name="TCommand">
/// Command type (e.g., an enum or a record/struct)
/// </typeparam>
/// <param name="command">
/// Command value to build
/// </param>
/// <returns>
/// Command bytes
/// </returns>
ReadOnlyMemory<byte> Build<TCommand>(TCommand command) where TCommand: notnull;
/// <summary>
/// Try-build variant to avoid throwing and allow buffer reuse strategies.
/// </summary>
bool TryBuild<TCommand>(TCommand command, out ReadOnlyMemory<byte> bytes) where TCommand: notnull;
}
}
namespace EM.LowLevel
{
/// <summary>
/// Builds device/peripheral commands from C#-friendly types (enums, records, structs, etc.).
/// </summary>
public interface ICommandBuilder
{
/// <summary>
/// Build a command payload (already framed or raw depending on implementation contract) from a
/// strongly-typed command description.
/// </summary>
/// <typeparam name="TCommand">
/// Command type (e.g., an enum or a record/struct)
/// </typeparam>
/// <param name="command">
/// Command value to build
/// </param>
/// <returns>
/// Command bytes
/// </returns>
ReadOnlyMemory<byte> Build<TCommand>(TCommand command) where TCommand: notnull;
/// <summary>
/// Try-build variant to avoid throwing and allow buffer reuse strategies.
/// </summary>
bool TryBuild<TCommand>(TCommand command, out ReadOnlyMemory<byte> bytes) where TCommand: notnull;
}
}
+29 -29
View File
@@ -1,29 +1,29 @@
namespace EM.LowLevel
{
/// <summary>
/// Decodes device-specific binary responses into C#-friendly models.
/// </summary>
public interface IDataDecoder
{
/// <summary>
/// Decode a response of a known type into a model.
/// </summary>
/// <typeparam name="TModel">
/// Target model type
/// </typeparam>
/// <param name="payload">
/// Raw or deframed payload depending on transport contract
/// </param>
/// <returns>
/// Decoded model
/// </returns>
TModel Decode<TModel>(ReadOnlySpan<byte> payload);
/// <summary>
/// Try-decode variant to avoid throwing on partial/bad frames.
/// </summary>
bool TryDecode<TModel>(ReadOnlySpan<byte> payload, out TModel model);
}
}
namespace EM.LowLevel
{
/// <summary>
/// Decodes device-specific binary responses into C#-friendly models.
/// </summary>
public interface IDataDecoder
{
/// <summary>
/// Decode a response of a known type into a model.
/// </summary>
/// <typeparam name="TModel">
/// Target model type
/// </typeparam>
/// <param name="payload">
/// Raw or deframed payload depending on transport contract
/// </param>
/// <returns>
/// Decoded model
/// </returns>
TModel Decode<TModel>(ReadOnlySpan<byte> payload);
/// <summary>
/// Try-decode variant to avoid throwing on partial/bad frames.
/// </summary>
bool TryDecode<TModel>(ReadOnlySpan<byte> payload, out TModel model);
}
}
+54 -54
View File
@@ -1,54 +1,54 @@
using System.Threading;
using System.Threading.Tasks;
namespace EM.LowLevel
{
/// <summary>
/// Generic high-level controller that composes a PortStream, an ICommandBuilder and an
/// IDataDecoder to provide a complete peripheral control workflow.
/// </summary>
/// <typeparam name="TPort">
/// Concrete transport deriving from PortStream
/// </typeparam>
/// <typeparam name="TBuilder">
/// Concrete command builder
/// </typeparam>
/// <typeparam name="TDecoder">
/// Concrete data decoder
/// </typeparam>
public interface IPortController<TPort, TBuilder, TDecoder> where TPort: PortStream where TBuilder: ICommandBuilder
where TDecoder: IDataDecoder
{
TBuilder CommandBuilder { get; }
TDecoder Decoder { get; }
TPort Port { get; }
/// <summary>
/// Close the underlying port.
/// </summary>
void Close();
/// <summary>
/// Ensure the underlying port is opened and ready (delegates to Port.OpenAsync).
/// </summary>
ValueTask OpenAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Build a command, send it via the port and decode the response into a model.
/// </summary>
ValueTask<TResponse> RoundTripAsync<TCommand, TResponse>(
TCommand command,
CancellationToken cancellationToken = default)
where TCommand: notnull;
/// <summary>
/// Build a command and send it without expecting a typed response.
/// </summary>
ValueTask WriteAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default)
where TCommand: notnull;
}
}
using System.Threading;
using System.Threading.Tasks;
namespace EM.LowLevel
{
/// <summary>
/// Generic high-level controller that composes a PortStream, an ICommandBuilder and an
/// IDataDecoder to provide a complete peripheral control workflow.
/// </summary>
/// <typeparam name="TPort">
/// Concrete transport deriving from PortStream
/// </typeparam>
/// <typeparam name="TBuilder">
/// Concrete command builder
/// </typeparam>
/// <typeparam name="TDecoder">
/// Concrete data decoder
/// </typeparam>
public interface IPortController<TPort, TBuilder, TDecoder> where TPort: PortStream where TBuilder: ICommandBuilder
where TDecoder: IDataDecoder
{
TBuilder CommandBuilder { get; }
TDecoder Decoder { get; }
TPort Port { get; }
/// <summary>
/// Close the underlying port.
/// </summary>
void Close();
/// <summary>
/// Ensure the underlying port is opened and ready (delegates to Port.OpenAsync).
/// </summary>
ValueTask OpenAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Build a command, send it via the port and decode the response into a model.
/// </summary>
ValueTask<TResponse> RoundTripAsync<TCommand, TResponse>(
TCommand command,
CancellationToken cancellationToken = default)
where TCommand: notnull;
/// <summary>
/// Build a command and send it without expecting a typed response.
/// </summary>
ValueTask WriteAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default)
where TCommand: notnull;
}
}
+7 -7
View File
@@ -1,7 +1,7 @@
namespace EM.LowLevel
{
/// <summary>
/// Marker interface for decoded response models returned by IDataDecoder.
/// </summary>
public interface IResponse { }
}
namespace EM.LowLevel
{
/// <summary>
/// Marker interface for decoded response models returned by IDataDecoder.
/// </summary>
public interface IResponse { }
}
+35 -35
View File
@@ -1,35 +1,35 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace EM.LowLevel
{
/// <summary>
/// High-level transport abstraction for device communication. Derives from Stream and
/// encapsulates initialization/handshake and frame (header/tail/checksum) handling as needed
/// by the device. Implementations may wrap SerialPort, Sockets, USB, etc.
/// </summary>
public abstract class PortStream : Stream
{
/// <summary>
/// Optional bytes available hint if the transport can provide it.
/// </summary>
public virtual long? BytesAvailable => null;
/// <summary>
/// Close the underlying transport and release resources.
/// </summary>
public abstract override void Close();
/// <summary>
/// Ensure the port is opened and ready. Idempotent. May perform device-specific
/// handshakes.
/// </summary>
public abstract ValueTask OpenAsync(CancellationToken cancellationToken = default);
// Concrete transports must implement Stream's abstract members.
// Seek-related members are typically not supported and should throw NotSupportedException.
}
}
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace EM.LowLevel
{
/// <summary>
/// High-level transport abstraction for device communication. Derives from Stream and
/// encapsulates initialization/handshake and frame (header/tail/checksum) handling as needed
/// by the device. Implementations may wrap SerialPort, Sockets, USB, etc.
/// </summary>
public abstract class PortStream : Stream
{
/// <summary>
/// Optional bytes available hint if the transport can provide it.
/// </summary>
public virtual long? BytesAvailable => null;
/// <summary>
/// Close the underlying transport and release resources.
/// </summary>
public abstract override void Close();
/// <summary>
/// Ensure the port is opened and ready. Idempotent. May perform device-specific
/// handshakes.
/// </summary>
public abstract ValueTask OpenAsync(CancellationToken cancellationToken = default);
// Concrete transports must implement Stream's abstract members.
// Seek-related members are typically not supported and should throw NotSupportedException.
}
}
+192 -192
View File
@@ -1,192 +1,192 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="CsTypeMapper"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
namespace GenerateGL.CodeGen
{
internal static class CsTypeMapper
{
public static string MapDelegateParameter(
GlParam param
)
{
int pointerLevel = param.PointerLevel;
if (param.CType.Equals("GLchar", StringComparison.Ordinal) && pointerLevel > 0) {
return $"byte{new string('*', pointerLevel)}";
}
string mapped = MapType(param.CType, allowVoid:false);
if (pointerLevel > 0)
{
if (mapped == "void") {
return $"void{new string('*', pointerLevel)}";
}
return $"{mapped}{new string('*', pointerLevel)}";
}
return mapped;
}
public static string MapDelegateReturn(
string cType,
int pointerLevel
)
{
string mapped = MapType(cType, allowVoid:true);
if (pointerLevel > 0) {
return $"{mapped}{new string('*', pointerLevel)}";
}
return mapped;
}
public static string MapParameter(
GlParam param
)
{
if (param.PointerLevel == 0)
{
if (param.CType.Equals("GLenum", StringComparison.Ordinal) &&
LooksLikeShaderType(param.Name) &&
!param.IsPointer) {
return "ShaderType";
}
if (param.CType.Equals("GLuint", StringComparison.Ordinal) &&
LooksLikeHandle(param.Name) &&
!param.IsPointer) {
return "GlHandle";
}
}
int pointerLevel = param.PointerLevel;
if (param.CType.Equals("GLchar", StringComparison.Ordinal) && pointerLevel > 0) {
return $"byte{new string('*', pointerLevel)}";
}
string mapped = MapType(param.CType, allowVoid:false);
if (pointerLevel > 0)
{
if (mapped == "void") {
return $"void{new string('*', pointerLevel)}";
}
return $"{mapped}{new string('*', pointerLevel)}";
}
return mapped;
}
public static string MapReturn(
string cType,
int pointerLevel
)
{
string mapped = MapType(cType, allowVoid:true);
if (pointerLevel > 0) {
return $"{mapped}{new string('*', pointerLevel)}";
}
return mapped;
}
public static bool TryMapConstPointerToSpan(
GlParam param,
out string spanElementType
)
{
spanElementType = string.Empty;
if (!param.IsConst || param.PointerLevel != 1) {
return false;
}
string mapped = MapType(param.CType, allowVoid:false);
if (mapped is "void") {
return false;
}
spanElementType = mapped;
return true;
}
private static bool LooksLikeHandle(
string name
)
{
if (string.IsNullOrWhiteSpace(name)) {
return false;
}
return name.Contains("program", StringComparison.OrdinalIgnoreCase) ||
name.Contains("shader", StringComparison.OrdinalIgnoreCase) ||
name.Contains("buffer", StringComparison.OrdinalIgnoreCase) ||
name.Contains("texture", StringComparison.OrdinalIgnoreCase) ||
name.Contains("framebuffer", StringComparison.OrdinalIgnoreCase) ||
name.Contains("renderbuffer", StringComparison.OrdinalIgnoreCase) ||
name.Contains("vertexarray", StringComparison.OrdinalIgnoreCase) ||
name.Contains("query", StringComparison.OrdinalIgnoreCase) ||
name.Contains("sampler", StringComparison.OrdinalIgnoreCase) ||
name.Contains("transformfeedback", StringComparison.OrdinalIgnoreCase);
}
private static bool LooksLikeShaderType(
string name
)
{
if (string.IsNullOrWhiteSpace(name)) {
return false;
}
return name.Contains("shadertype", StringComparison.OrdinalIgnoreCase) ||
name.Equals("type", StringComparison.OrdinalIgnoreCase);
}
private static string MapType(
string cType,
bool allowVoid
)
{
return cType switch
{
"void" when allowVoid => "void",
"GLenum" or "GLbitfield" => "uint",
"GLboolean" => "byte",
"GLuint" or "GLhandleARB" => "uint",
"GLint" or "GLsizei" or "GLfixed" => "int",
"GLshort" => "short",
"GLbyte" => "sbyte",
"GLubyte" => "byte",
"GLushort" => "ushort",
"GLfloat" => "float",
"GLdouble" => "double",
"GLclampf" or "GLclampd" => "float",
"GLintptr" => "nint",
"GLsizeiptr" => "nuint",
"GLchar" => "byte",
"GLsync" => "GLsync",
_ => allowVoid && cType == "void" ? "void" : "IntPtr"
};
}
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="CsTypeMapper"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
namespace GenerateGL.CodeGen
{
internal static class CsTypeMapper
{
public static string MapDelegateParameter(
GlParam param
)
{
int pointerLevel = param.PointerLevel;
if (param.CType.Equals("GLchar", StringComparison.Ordinal) && pointerLevel > 0) {
return $"byte{new string('*', pointerLevel)}";
}
string mapped = MapType(param.CType, allowVoid:false);
if (pointerLevel > 0)
{
if (mapped == "void") {
return $"void{new string('*', pointerLevel)}";
}
return $"{mapped}{new string('*', pointerLevel)}";
}
return mapped;
}
public static string MapDelegateReturn(
string cType,
int pointerLevel
)
{
string mapped = MapType(cType, allowVoid:true);
if (pointerLevel > 0) {
return $"{mapped}{new string('*', pointerLevel)}";
}
return mapped;
}
public static string MapParameter(
GlParam param
)
{
if (param.PointerLevel == 0)
{
if (param.CType.Equals("GLenum", StringComparison.Ordinal) &&
LooksLikeShaderType(param.Name) &&
!param.IsPointer) {
return "ShaderType";
}
if (param.CType.Equals("GLuint", StringComparison.Ordinal) &&
LooksLikeHandle(param.Name) &&
!param.IsPointer) {
return "GlHandle";
}
}
int pointerLevel = param.PointerLevel;
if (param.CType.Equals("GLchar", StringComparison.Ordinal) && pointerLevel > 0) {
return $"byte{new string('*', pointerLevel)}";
}
string mapped = MapType(param.CType, allowVoid:false);
if (pointerLevel > 0)
{
if (mapped == "void") {
return $"void{new string('*', pointerLevel)}";
}
return $"{mapped}{new string('*', pointerLevel)}";
}
return mapped;
}
public static string MapReturn(
string cType,
int pointerLevel
)
{
string mapped = MapType(cType, allowVoid:true);
if (pointerLevel > 0) {
return $"{mapped}{new string('*', pointerLevel)}";
}
return mapped;
}
public static bool TryMapConstPointerToSpan(
GlParam param,
out string spanElementType
)
{
spanElementType = string.Empty;
if (!param.IsConst || param.PointerLevel != 1) {
return false;
}
string mapped = MapType(param.CType, allowVoid:false);
if (mapped is "void") {
return false;
}
spanElementType = mapped;
return true;
}
private static bool LooksLikeHandle(
string name
)
{
if (string.IsNullOrWhiteSpace(name)) {
return false;
}
return name.Contains("program", StringComparison.OrdinalIgnoreCase) ||
name.Contains("shader", StringComparison.OrdinalIgnoreCase) ||
name.Contains("buffer", StringComparison.OrdinalIgnoreCase) ||
name.Contains("texture", StringComparison.OrdinalIgnoreCase) ||
name.Contains("framebuffer", StringComparison.OrdinalIgnoreCase) ||
name.Contains("renderbuffer", StringComparison.OrdinalIgnoreCase) ||
name.Contains("vertexarray", StringComparison.OrdinalIgnoreCase) ||
name.Contains("query", StringComparison.OrdinalIgnoreCase) ||
name.Contains("sampler", StringComparison.OrdinalIgnoreCase) ||
name.Contains("transformfeedback", StringComparison.OrdinalIgnoreCase);
}
private static bool LooksLikeShaderType(
string name
)
{
if (string.IsNullOrWhiteSpace(name)) {
return false;
}
return name.Contains("shadertype", StringComparison.OrdinalIgnoreCase) ||
name.Equals("type", StringComparison.OrdinalIgnoreCase);
}
private static string MapType(
string cType,
bool allowVoid
)
{
return cType switch
{
"void" when allowVoid => "void",
"GLenum" or "GLbitfield" => "uint",
"GLboolean" => "byte",
"GLuint" or "GLhandleARB" => "uint",
"GLint" or "GLsizei" or "GLfixed" => "int",
"GLshort" => "short",
"GLbyte" => "sbyte",
"GLubyte" => "byte",
"GLushort" => "ushort",
"GLfloat" => "float",
"GLdouble" => "double",
"GLclampf" or "GLclampd" => "float",
"GLintptr" => "nint",
"GLsizeiptr" => "nuint",
"GLchar" => "byte",
"GLsync" => "GLsync",
_ => allowVoid && cType == "void" ? "void" : "IntPtr"
};
}
}
}
+422 -422
View File
@@ -1,422 +1,422 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="Generator"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
namespace GenerateGL.CodeGen
{
internal sealed class Generator
{
private static readonly string[] TargetVersions = {
"3.3", "4.0", "4.1", "4.2", "4.3", "4.4", "4.5", "4.6"
};
private static readonly FrozenDictionary<string, string> ReservedNames =
new Dictionary<string, string>(StringComparer.Ordinal) {
{ "string", "@string" }, { "class", "@class" }, { "namespace", "@namespace" }, { "checked", "@checked" }, { "unchecked", "@unchecked" }, { "ref", "@ref" }, { "out", "@out" }, { "in", "@in" }, { "object", "@object" }, { "event", "@event" }, { "params", "@params" }, { "base", "@base" }, { "this", "@this" }, { "void", "@void" }, { "int", "@int" }, { "uint", "@uint" }, { "long", "@long" }, { "ulong", "@ulong" }, { "short", "@short" }, { "ushort", "@ushort" }, { "float", "@float" }, { "double", "@double" }, { "decimal", "@decimal" }, { "bool", "@bool" }, { "byte", "@byte" }, { "sbyte", "@sbyte" }, { "char", "@char" }, { "struct", "@struct" }, { "public", "@public" }, { "private", "@private" }, { "protected", "@protected" }, { "internal", "@internal" }, { "fixed", "@fixed" }
}.ToFrozenDictionary(StringComparer.Ordinal);
private readonly IReadOnlyDictionary<string, GlCommand> _commands;
private readonly IReadOnlyList<GlFeature> _features;
private readonly string _outputRoot;
public Generator(
IReadOnlyDictionary<string, GlCommand> commands,
IReadOnlyList<GlFeature> features,
string outputRoot
)
{
_commands = commands;
_features = features;
_outputRoot = outputRoot;
}
public void Run()
{
if (Directory.Exists(_outputRoot)) {
Directory.Delete(_outputRoot, recursive:true);
}
Directory.CreateDirectory(_outputRoot);
List<GlCommand> all = new List<GlCommand>();
HashSet<string> previous = new HashSet<string>(StringComparer.Ordinal);
foreach (string v in TargetVersions)
{
IReadOnlyList<GlCommand> cmdsUpTo = CollectCommandsUpToVersion(v);
List<GlCommand> versionCmds = cmdsUpTo.Where(c => !previous.Contains(c.Name))
.ToList();
foreach (GlCommand cmd in versionCmds) {
previous.Add(cmd.Name);
}
all.AddRange(versionCmds);
WriteOpenGLFunctionFile(v, versionCmds);
WriteGlContextFile(v, versionCmds);
}
WriteMergedGlContextFile(all);
WriteCommandHistoryFile(BuildCommandHistory());
}
private IReadOnlyDictionary<string, List<CommandHistoryEntry>> BuildCommandHistory()
{
Dictionary<string, List<CommandHistoryEntry>> history = new(StringComparer.Ordinal);
IEnumerable<GlFeature> ordered = _features.Where(f => Version.TryParse(f.Version, out _))
.OrderBy(f => Version.Parse(f.Version));
foreach (GlFeature feature in ordered)
{
string version = feature.Version;
foreach (string name in feature.RequiresCommands.Distinct(StringComparer.Ordinal))
{
if (!_commands.ContainsKey(name) || ShouldSkip(name))
{
continue;
}
if (!history.TryGetValue(name, out List<CommandHistoryEntry>? list))
{
list = new List<CommandHistoryEntry>();
history[name] = list;
}
if (list.Count == 0 ||
list[^1].Action != CommandHistoryAction.Added ||
list[^1].Version != version) {
list.Add(new CommandHistoryEntry(version, CommandHistoryAction.Added));
}
}
foreach (string name in feature.RemovedCommands.Distinct(StringComparer.Ordinal))
{
if (!_commands.ContainsKey(name) || ShouldSkip(name))
{
continue;
}
if (!history.TryGetValue(name, out List<CommandHistoryEntry>? list))
{
list = new List<CommandHistoryEntry>();
history[name] = list;
}
if (list.Count == 0 ||
list[^1].Action != CommandHistoryAction.Removed ||
list[^1].Version != version) {
list.Add(new CommandHistoryEntry(version, CommandHistoryAction.Removed));
}
}
}
return history;
}
private IReadOnlyList<GlCommand> CollectCommandsUpToVersion(
string version
)
{
Version target = Version.Parse(version);
IEnumerable<GlFeature> ordered = _features.Where(f => Version.TryParse(f.Version, out Version? fv) &&
fv <= target)
.OrderBy(f => Version.Parse(f.Version));
HashSet<string> active = new HashSet<string>(StringComparer.Ordinal);
foreach (GlFeature feature in ordered)
{
foreach (string name in feature.RequiresCommands.Distinct(StringComparer.Ordinal))
{
if (_commands.ContainsKey(name)) {
active.Add(name);
}
}
foreach (string name in feature.RemovedCommands.Distinct(StringComparer.Ordinal)) {
active.Remove(name);
}
}
return active.Select(name => _commands.TryGetValue(name, out GlCommand? c) ? c : null)
.Where(c => c != null)!
.Cast<GlCommand>()
.ToList();
}
private static string DelegateSignature(
string ret,
IReadOnlyList<string> paramTypes
)
{
if (paramTypes.Count == 0) {
return $"delegate*<{ret}>";
}
return $"delegate*<{string.Join(", ", paramTypes)}, {ret}>";
}
private static string SafeName(
string name
)
{
if (string.IsNullOrWhiteSpace(name)) {
return "arg";
}
if (ReservedNames.TryGetValue(name, out string? mapped)) {
return mapped;
}
return name;
}
private static bool ShouldSkip(
string name
)
{
// No skipping; include legacy/fixed-function APIs as well.
return false;
}
private static string TrimGlPrefix(
string name
)
{
if (name.StartsWith("gl", StringComparison.Ordinal) && name.Length > 2) {
return name.Substring(2);
}
return name;
}
private void WriteCommandHistoryFile(
IReadOnlyDictionary<string, List<CommandHistoryEntry>> history
)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("// <auto-generated>");
sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}");
sb.AppendLine("// Command availability history");
sb.AppendLine("// </auto-generated>");
sb.AppendLine("using System;\nusing System.Collections.Frozen;\nusing System.Collections.Generic;\n");
sb.AppendLine("namespace StgSharp.Graphics.OpenGL");
sb.AppendLine("{");
sb.AppendLine(" internal enum CommandHistoryAction { Added, Removed }");
sb.AppendLine();
sb.AppendLine(" internal readonly record struct CommandHistoryEntry(string Version, CommandHistoryAction Action);");
sb.AppendLine();
sb.AppendLine(" internal static class GlCommandHistory");
sb.AppendLine(" {");
sb.AppendLine(" internal static readonly FrozenDictionary<string, CommandHistoryEntry[]> History = new Dictionary<string, CommandHistoryEntry[]>(StringComparer.Ordinal)");
sb.AppendLine(" {");
foreach (KeyValuePair<string, List<CommandHistoryEntry>> kvp in history.OrderBy(k => k.Key, StringComparer.Ordinal))
{
string events = string.Join(", ", kvp.Value
.OrderBy(v => Version.Parse(v.Version))
.Select(v => $"new CommandHistoryEntry(\"{v.Version}\", CommandHistoryAction.{v.Action})"));
sb.AppendLine($" {{ \"{kvp.Key}\", new CommandHistoryEntry[] {{ {events} }} }},");
}
sb.AppendLine(" }.ToFrozenDictionary(StringComparer.Ordinal);");
sb.AppendLine(" }");
sb.AppendLine("}");
string fileName = Path.Combine(_outputRoot, "glCommandHistory.cs");
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
}
private void WriteGlContextFile(
string version,
IReadOnlyList<GlCommand> commands
)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("// <auto-generated>");
sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}");
sb.AppendLine($"// Version: {version}");
sb.AppendLine("// </auto-generated>");
sb.AppendLine("using System;\nusing System.Runtime.CompilerServices;\n");
sb.AppendLine("namespace StgSharp.Graphics.OpenGL");
sb.AppendLine("{");
sb.AppendLine(" public unsafe partial struct OpenglContext");
sb.AppendLine(" {");
foreach (GlCommand cmd in commands.OrderBy(c => c!.Name, StringComparer.Ordinal))
{
string ret = CsTypeMapper.MapDelegateReturn(cmd.ReturnType, cmd.ReturnPointerLevel);
List<string> paramTypes = new List<string>();
foreach (GlParam p in cmd.Parameters)
{
string mapped = CsTypeMapper.MapDelegateParameter(p);
paramTypes.Add(mapped);
}
string decl = DelegateSignature(ret, paramTypes);
sb.AppendLine($" internal unsafe {decl} {cmd.Name};");
}
sb.AppendLine(" }");
sb.AppendLine("}");
string fileName = Path.Combine(_outputRoot, $"glContext.v{version.Replace('.', '_')}.cs");
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
}
private void WriteMergedGlContextFile(
IReadOnlyList<GlCommand> commands
)
{
List<GlCommand> distinct = commands.Where(c => !ShouldSkip(c.Name))
.GroupBy(c => c.Name, StringComparer.Ordinal)
.Select(g => g.First())
.OrderBy(c => c.Name, StringComparer.Ordinal)
.ToList();
StringBuilder sb = new StringBuilder();
sb.AppendLine("// <auto-generated>");
sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}");
sb.AppendLine("// Aggregated GL3+ commands");
sb.AppendLine("// </auto-generated>");
sb.AppendLine("using System;\nusing System.Runtime.CompilerServices;\n");
sb.AppendLine("namespace StgSharp.Graphics.OpenGL");
sb.AppendLine("{");
sb.AppendLine(" public unsafe partial struct OpenglContext");
sb.AppendLine(" {");
foreach (GlCommand cmd in distinct)
{
string ret = CsTypeMapper.MapDelegateReturn(cmd.ReturnType, cmd.ReturnPointerLevel);
List<string> paramTypes = new List<string>();
foreach (GlParam p in cmd.Parameters)
{
string mapped = CsTypeMapper.MapDelegateParameter(p);
paramTypes.Add(mapped);
}
string decl = DelegateSignature(ret, paramTypes);
sb.AppendLine($" internal unsafe {decl} {cmd.Name};");
}
sb.AppendLine(" }");
sb.AppendLine("}");
string fileName = Path.Combine(_outputRoot, "glContext.cs");
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
}
private void WriteOpenGLFunctionFile(
string version,
IReadOnlyList<GlCommand> commands
)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("// <auto-generated>");
sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}");
sb.AppendLine($"// Version: {version}");
sb.AppendLine("// </auto-generated>");
sb.AppendLine("using System;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\nusing StgSharp.Graphics.OpenGL;\n");
sb.AppendLine("namespace StgSharp.Graphics.OpenGL");
sb.AppendLine("{");
sb.AppendLine(" public unsafe partial class OpenGLFunction");
sb.AppendLine(" {");
foreach (GlCommand cmd in commands.OrderBy(c => c!.Name, StringComparer.Ordinal))
{
string ret = CsTypeMapper.MapReturn(cmd.ReturnType, cmd.ReturnPointerLevel);
string rawName = cmd.Name; // original glXxx
string methodName = TrimGlPrefix(rawName);
List<string> paramList = new List<string>();
List<string> callArgs = new List<string>();
List<(string Type,string Name)> pinned = new();
foreach (GlParam p in cmd.Parameters)
{
string pname = SafeName(p.Name);
if (CsTypeMapper.TryMapConstPointerToSpan(p, out string spanElementType))
{
paramList.Add($"ReadOnlySpan<{spanElementType}> {pname}");
pinned.Add((spanElementType, pname));
callArgs.Add($"{pname}Ptr");
} else
{
string mapped = CsTypeMapper.MapParameter(p);
paramList.Add($"{mapped} {pname}");
if (mapped == "GlHandle")
{
callArgs.Add($"{pname}.Value");
} else
{
callArgs.Add(pname);
}
}
}
string signature = string.Join(", ", paramList);
string call = string.Join(", ", callArgs);
sb.AppendLine($" [MethodImpl(MethodImplOptions.AggressiveInlining)]");
sb.AppendLine($" public {ret} {methodName}({signature})");
sb.AppendLine(" {");
string prefix = ret == "void" ? string.Empty : "return ";
if (pinned.Count == 0)
{
sb.AppendLine($" {prefix}Context.{rawName}({call});");
} else
{
string indent = " ";
foreach ((string Type, string Name) pin in pinned)
{
sb.AppendLine($"{indent}fixed ({pin.Type}* {pin.Name}Ptr = {pin.Name})");
sb.AppendLine($"{indent}{{");
indent += " ";
}
sb.AppendLine($"{indent}{prefix}Context.{rawName}({call});");
for (int i = 0; i < pinned.Count; i++)
{
indent = indent.Substring(0, indent.Length - 4);
sb.AppendLine($"{indent}}}");
}
}
sb.AppendLine(" }");
sb.AppendLine();
}
sb.AppendLine(" }");
sb.AppendLine("}");
string fileName = Path.Combine(_outputRoot, $"glFunction.v{version.Replace('.', '_')}.cs");
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
}
private enum CommandHistoryAction
{
Added,
Removed
}
private readonly record struct CommandHistoryEntry(
string Version,
CommandHistoryAction Action
);
}
}
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="Generator"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// 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.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
namespace GenerateGL.CodeGen
{
internal sealed class Generator
{
private static readonly string[] TargetVersions = {
"3.3", "4.0", "4.1", "4.2", "4.3", "4.4", "4.5", "4.6"
};
private static readonly FrozenDictionary<string, string> ReservedNames =
new Dictionary<string, string>(StringComparer.Ordinal) {
{ "string", "@string" }, { "class", "@class" }, { "namespace", "@namespace" }, { "checked", "@checked" }, { "unchecked", "@unchecked" }, { "ref", "@ref" }, { "out", "@out" }, { "in", "@in" }, { "object", "@object" }, { "event", "@event" }, { "params", "@params" }, { "base", "@base" }, { "this", "@this" }, { "void", "@void" }, { "int", "@int" }, { "uint", "@uint" }, { "long", "@long" }, { "ulong", "@ulong" }, { "short", "@short" }, { "ushort", "@ushort" }, { "float", "@float" }, { "double", "@double" }, { "decimal", "@decimal" }, { "bool", "@bool" }, { "byte", "@byte" }, { "sbyte", "@sbyte" }, { "char", "@char" }, { "struct", "@struct" }, { "public", "@public" }, { "private", "@private" }, { "protected", "@protected" }, { "internal", "@internal" }, { "fixed", "@fixed" }
}.ToFrozenDictionary(StringComparer.Ordinal);
private readonly IReadOnlyDictionary<string, GlCommand> _commands;
private readonly IReadOnlyList<GlFeature> _features;
private readonly string _outputRoot;
public Generator(
IReadOnlyDictionary<string, GlCommand> commands,
IReadOnlyList<GlFeature> features,
string outputRoot
)
{
_commands = commands;
_features = features;
_outputRoot = outputRoot;
}
public void Run()
{
if (Directory.Exists(_outputRoot)) {
Directory.Delete(_outputRoot, recursive:true);
}
Directory.CreateDirectory(_outputRoot);
List<GlCommand> all = new List<GlCommand>();
HashSet<string> previous = new HashSet<string>(StringComparer.Ordinal);
foreach (string v in TargetVersions)
{
IReadOnlyList<GlCommand> cmdsUpTo = CollectCommandsUpToVersion(v);
List<GlCommand> versionCmds = cmdsUpTo.Where(c => !previous.Contains(c.Name))
.ToList();
foreach (GlCommand cmd in versionCmds) {
previous.Add(cmd.Name);
}
all.AddRange(versionCmds);
WriteOpenGLFunctionFile(v, versionCmds);
WriteGlContextFile(v, versionCmds);
}
WriteMergedGlContextFile(all);
WriteCommandHistoryFile(BuildCommandHistory());
}
private IReadOnlyDictionary<string, List<CommandHistoryEntry>> BuildCommandHistory()
{
Dictionary<string, List<CommandHistoryEntry>> history = new(StringComparer.Ordinal);
IEnumerable<GlFeature> ordered = _features.Where(f => Version.TryParse(f.Version, out _))
.OrderBy(f => Version.Parse(f.Version));
foreach (GlFeature feature in ordered)
{
string version = feature.Version;
foreach (string name in feature.RequiresCommands.Distinct(StringComparer.Ordinal))
{
if (!_commands.ContainsKey(name) || ShouldSkip(name))
{
continue;
}
if (!history.TryGetValue(name, out List<CommandHistoryEntry>? list))
{
list = new List<CommandHistoryEntry>();
history[name] = list;
}
if (list.Count == 0 ||
list[^1].Action != CommandHistoryAction.Added ||
list[^1].Version != version) {
list.Add(new CommandHistoryEntry(version, CommandHistoryAction.Added));
}
}
foreach (string name in feature.RemovedCommands.Distinct(StringComparer.Ordinal))
{
if (!_commands.ContainsKey(name) || ShouldSkip(name))
{
continue;
}
if (!history.TryGetValue(name, out List<CommandHistoryEntry>? list))
{
list = new List<CommandHistoryEntry>();
history[name] = list;
}
if (list.Count == 0 ||
list[^1].Action != CommandHistoryAction.Removed ||
list[^1].Version != version) {
list.Add(new CommandHistoryEntry(version, CommandHistoryAction.Removed));
}
}
}
return history;
}
private IReadOnlyList<GlCommand> CollectCommandsUpToVersion(
string version
)
{
Version target = Version.Parse(version);
IEnumerable<GlFeature> ordered = _features.Where(f => Version.TryParse(f.Version, out Version? fv) &&
fv <= target)
.OrderBy(f => Version.Parse(f.Version));
HashSet<string> active = new HashSet<string>(StringComparer.Ordinal);
foreach (GlFeature feature in ordered)
{
foreach (string name in feature.RequiresCommands.Distinct(StringComparer.Ordinal))
{
if (_commands.ContainsKey(name)) {
active.Add(name);
}
}
foreach (string name in feature.RemovedCommands.Distinct(StringComparer.Ordinal)) {
active.Remove(name);
}
}
return active.Select(name => _commands.TryGetValue(name, out GlCommand? c) ? c : null)
.Where(c => c != null)!
.Cast<GlCommand>()
.ToList();
}
private static string DelegateSignature(
string ret,
IReadOnlyList<string> paramTypes
)
{
if (paramTypes.Count == 0) {
return $"delegate*<{ret}>";
}
return $"delegate*<{string.Join(", ", paramTypes)}, {ret}>";
}
private static string SafeName(
string name
)
{
if (string.IsNullOrWhiteSpace(name)) {
return "arg";
}
if (ReservedNames.TryGetValue(name, out string? mapped)) {
return mapped;
}
return name;
}
private static bool ShouldSkip(
string name
)
{
// No skipping; include legacy/fixed-function APIs as well.
return false;
}
private static string TrimGlPrefix(
string name
)
{
if (name.StartsWith("gl", StringComparison.Ordinal) && name.Length > 2) {
return name.Substring(2);
}
return name;
}
private void WriteCommandHistoryFile(
IReadOnlyDictionary<string, List<CommandHistoryEntry>> history
)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("// <auto-generated>");
sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}");
sb.AppendLine("// Command availability history");
sb.AppendLine("// </auto-generated>");
sb.AppendLine("using System;\nusing System.Collections.Frozen;\nusing System.Collections.Generic;\n");
sb.AppendLine("namespace StgSharp.Graphics.OpenGL");
sb.AppendLine("{");
sb.AppendLine(" internal enum CommandHistoryAction { Added, Removed }");
sb.AppendLine();
sb.AppendLine(" internal readonly record struct CommandHistoryEntry(string Version, CommandHistoryAction Action);");
sb.AppendLine();
sb.AppendLine(" internal static class GlCommandHistory");
sb.AppendLine(" {");
sb.AppendLine(" internal static readonly FrozenDictionary<string, CommandHistoryEntry[]> History = new Dictionary<string, CommandHistoryEntry[]>(StringComparer.Ordinal)");
sb.AppendLine(" {");
foreach (KeyValuePair<string, List<CommandHistoryEntry>> kvp in history.OrderBy(k => k.Key, StringComparer.Ordinal))
{
string events = string.Join(", ", kvp.Value
.OrderBy(v => Version.Parse(v.Version))
.Select(v => $"new CommandHistoryEntry(\"{v.Version}\", CommandHistoryAction.{v.Action})"));
sb.AppendLine($" {{ \"{kvp.Key}\", new CommandHistoryEntry[] {{ {events} }} }},");
}
sb.AppendLine(" }.ToFrozenDictionary(StringComparer.Ordinal);");
sb.AppendLine(" }");
sb.AppendLine("}");
string fileName = Path.Combine(_outputRoot, "glCommandHistory.cs");
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
}
private void WriteGlContextFile(
string version,
IReadOnlyList<GlCommand> commands
)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("// <auto-generated>");
sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}");
sb.AppendLine($"// Version: {version}");
sb.AppendLine("// </auto-generated>");
sb.AppendLine("using System;\nusing System.Runtime.CompilerServices;\n");
sb.AppendLine("namespace StgSharp.Graphics.OpenGL");
sb.AppendLine("{");
sb.AppendLine(" public unsafe partial struct OpenglContext");
sb.AppendLine(" {");
foreach (GlCommand cmd in commands.OrderBy(c => c!.Name, StringComparer.Ordinal))
{
string ret = CsTypeMapper.MapDelegateReturn(cmd.ReturnType, cmd.ReturnPointerLevel);
List<string> paramTypes = new List<string>();
foreach (GlParam p in cmd.Parameters)
{
string mapped = CsTypeMapper.MapDelegateParameter(p);
paramTypes.Add(mapped);
}
string decl = DelegateSignature(ret, paramTypes);
sb.AppendLine($" internal unsafe {decl} {cmd.Name};");
}
sb.AppendLine(" }");
sb.AppendLine("}");
string fileName = Path.Combine(_outputRoot, $"glContext.v{version.Replace('.', '_')}.cs");
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
}
private void WriteMergedGlContextFile(
IReadOnlyList<GlCommand> commands
)
{
List<GlCommand> distinct = commands.Where(c => !ShouldSkip(c.Name))
.GroupBy(c => c.Name, StringComparer.Ordinal)
.Select(g => g.First())
.OrderBy(c => c.Name, StringComparer.Ordinal)
.ToList();
StringBuilder sb = new StringBuilder();
sb.AppendLine("// <auto-generated>");
sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}");
sb.AppendLine("// Aggregated GL3+ commands");
sb.AppendLine("// </auto-generated>");
sb.AppendLine("using System;\nusing System.Runtime.CompilerServices;\n");
sb.AppendLine("namespace StgSharp.Graphics.OpenGL");
sb.AppendLine("{");
sb.AppendLine(" public unsafe partial struct OpenglContext");
sb.AppendLine(" {");
foreach (GlCommand cmd in distinct)
{
string ret = CsTypeMapper.MapDelegateReturn(cmd.ReturnType, cmd.ReturnPointerLevel);
List<string> paramTypes = new List<string>();
foreach (GlParam p in cmd.Parameters)
{
string mapped = CsTypeMapper.MapDelegateParameter(p);
paramTypes.Add(mapped);
}
string decl = DelegateSignature(ret, paramTypes);
sb.AppendLine($" internal unsafe {decl} {cmd.Name};");
}
sb.AppendLine(" }");
sb.AppendLine("}");
string fileName = Path.Combine(_outputRoot, "glContext.cs");
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
}
private void WriteOpenGLFunctionFile(
string version,
IReadOnlyList<GlCommand> commands
)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("// <auto-generated>");
sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}");
sb.AppendLine($"// Version: {version}");
sb.AppendLine("// </auto-generated>");
sb.AppendLine("using System;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\nusing StgSharp.Graphics.OpenGL;\n");
sb.AppendLine("namespace StgSharp.Graphics.OpenGL");
sb.AppendLine("{");
sb.AppendLine(" public unsafe partial class OpenGLFunction");
sb.AppendLine(" {");
foreach (GlCommand cmd in commands.OrderBy(c => c!.Name, StringComparer.Ordinal))
{
string ret = CsTypeMapper.MapReturn(cmd.ReturnType, cmd.ReturnPointerLevel);
string rawName = cmd.Name; // original glXxx
string methodName = TrimGlPrefix(rawName);
List<string> paramList = new List<string>();
List<string> callArgs = new List<string>();
List<(string Type,string Name)> pinned = new();
foreach (GlParam p in cmd.Parameters)
{
string pname = SafeName(p.Name);
if (CsTypeMapper.TryMapConstPointerToSpan(p, out string spanElementType))
{
paramList.Add($"ReadOnlySpan<{spanElementType}> {pname}");
pinned.Add((spanElementType, pname));
callArgs.Add($"{pname}Ptr");
} else
{
string mapped = CsTypeMapper.MapParameter(p);
paramList.Add($"{mapped} {pname}");
if (mapped == "GlHandle")
{
callArgs.Add($"{pname}.Value");
} else
{
callArgs.Add(pname);
}
}
}
string signature = string.Join(", ", paramList);
string call = string.Join(", ", callArgs);
sb.AppendLine($" [MethodImpl(MethodImplOptions.AggressiveInlining)]");
sb.AppendLine($" public {ret} {methodName}({signature})");
sb.AppendLine(" {");
string prefix = ret == "void" ? string.Empty : "return ";
if (pinned.Count == 0)
{
sb.AppendLine($" {prefix}Context.{rawName}({call});");
} else
{
string indent = " ";
foreach ((string Type, string Name) pin in pinned)
{
sb.AppendLine($"{indent}fixed ({pin.Type}* {pin.Name}Ptr = {pin.Name})");
sb.AppendLine($"{indent}{{");
indent += " ";
}
sb.AppendLine($"{indent}{prefix}Context.{rawName}({call});");
for (int i = 0; i < pinned.Count; i++)
{
indent = indent.Substring(0, indent.Length - 4);
sb.AppendLine($"{indent}}}");
}
}
sb.AppendLine(" }");
sb.AppendLine();
}
sb.AppendLine(" }");
sb.AppendLine("}");
string fileName = Path.Combine(_outputRoot, $"glFunction.v{version.Replace('.', '_')}.cs");
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
}
private enum CommandHistoryAction
{
Added,
Removed
}
private readonly record struct CommandHistoryEntry(
string Version,
CommandHistoryAction Action
);
}
}

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