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"?> <?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.IntelliRush.Options.IntelliRushOptions"> <Root Type="DevExpress.IntelliRush.Options.IntelliRushOptions">
<Options Language="Neutral"> <Options Language="Neutral">
<Enabled>True</Enabled> <Enabled>True</Enabled>
<DisabledLanguages /> <DisabledLanguages />
</Options> </Options>
</Root> </Root>
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.VisualizePlugins.RightMarginLine.Options.RightMarginLineOptions"> <Root Type="DevExpress.CodeRush.VisualizePlugins.RightMarginLine.Options.RightMarginLineOptions">
<Options Language="Neutral"> <Options Language="Neutral">
<Enabled>True</Enabled> <Enabled>True</Enabled>
<Style>Dot</Style> <Style>Dot</Style>
<Width>2</Width> <Width>2</Width>
</Options> </Options>
</Root> </Root>
+5 -5
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.VSFeatures.SpellChecker.Options.SpellCheckerOptions"> <Root Type="DevExpress.CodeRush.VSFeatures.SpellChecker.Options.SpellCheckerOptions">
<Options Language="Neutral"> <Options Language="Neutral">
<SupportedElements>XmlComments|Comments|Strings|VerbatimStrings|PublicIdentifiers|InternalIdentifiers|ProtectedInternalIdentifiers|ProtectedIdentifiers|PrivateIdentifiers|LocalIdentifiers|FileNames</SupportedElements> <SupportedElements>XmlComments|Comments|Strings|VerbatimStrings|PublicIdentifiers|InternalIdentifiers|ProtectedInternalIdentifiers|ProtectedIdentifiers|PrivateIdentifiers|LocalIdentifiers|FileNames</SupportedElements>
</Options> </Options>
</Root> </Root>
+5 -5
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.Foundation.CodePlaces.Options.FavoritesListContainer"> <Root Type="DevExpress.CodeRush.Foundation.CodePlaces.Options.FavoritesListContainer">
<Options Language="Neutral"> <Options Language="Neutral">
<Groups /> <Groups />
</Options> </Options>
</Root> </Root>
+5 -5
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.VisualStudio.Services.Options.BuildEngineOptions"> <Root Type="DevExpress.CodeRush.VisualStudio.Services.Options.BuildEngineOptions">
<Options Language="Neutral"> <Options Language="Neutral">
<Enabled>True</Enabled> <Enabled>True</Enabled>
</Options> </Options>
</Root> </Root>
+57 -57
View File
@@ -1,58 +1,58 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.Foundation.Shortcuts.ShortcutsOptions"> <Root Type="DevExpress.CodeRush.Foundation.Shortcuts.ShortcutsOptions">
<Options Language="Neutral"> <Options Language="Neutral">
<RootFolder> <RootFolder>
<SubFolders> <SubFolders>
<Item Id="{01ea3896-6cc1-4912-824e-88c7c5a82a25}"> <Item Id="{01ea3896-6cc1-4912-824e-88c7c5a82a25}">
<Bindings> <Bindings>
<Item Id="{88959174-9ffa-429b-bef8-aeddb65b9c2a}" /> <Item Id="{88959174-9ffa-429b-bef8-aeddb65b9c2a}" />
<Item Id="{975814d0-802e-4143-a979-22d3f975cfc2}" /> <Item Id="{975814d0-802e-4143-a979-22d3f975cfc2}" />
<Item Id="{0260c6ac-cfdb-4874-88c5-66bbf9b84e9b}" /> <Item Id="{0260c6ac-cfdb-4874-88c5-66bbf9b84e9b}" />
<Item Id="{dd4ceb84-80b3-4517-ac9b-170dcffe646f}" /> <Item Id="{dd4ceb84-80b3-4517-ac9b-170dcffe646f}" />
<Item Id="{a1f4a77e-b674-41bd-880f-195c3c0c1dfe}" /> <Item Id="{a1f4a77e-b674-41bd-880f-195c3c0c1dfe}" />
<Item Id="{ee0b4600-2d2c-4700-8d4d-578027321f74}" /> <Item Id="{ee0b4600-2d2c-4700-8d4d-578027321f74}" />
<Item Id="{d37c2c40-a7c6-403f-8556-8fb690d4c656}"> <Item Id="{d37c2c40-a7c6-403f-8556-8fb690d4c656}">
<Enabled>False</Enabled> <Enabled>False</Enabled>
</Item> </Item>
<Item Id="{baf8010d-02d7-4ac9-b644-0c670f796ce4}" /> <Item Id="{baf8010d-02d7-4ac9-b644-0c670f796ce4}" />
<Item Id="{676742ae-19ea-4884-840d-2be66d9bb49c}" /> <Item Id="{676742ae-19ea-4884-840d-2be66d9bb49c}" />
<Item Id="{43d481e0-19e4-405d-b7e3-687fc610050c}"> <Item Id="{43d481e0-19e4-405d-b7e3-687fc610050c}">
<Enabled>False</Enabled> <Enabled>False</Enabled>
</Item> </Item>
<Item Id="{f1a06e4e-3241-438d-84e5-044e3e907492}" /> <Item Id="{f1a06e4e-3241-438d-84e5-044e3e907492}" />
<Item Id="{3b8cae25-e23f-443c-98ff-83d38711f26e}" /> <Item Id="{3b8cae25-e23f-443c-98ff-83d38711f26e}" />
<Item Id="{491253a6-324c-4a0f-b4e5-aff443b00fb9}" /> <Item Id="{491253a6-324c-4a0f-b4e5-aff443b00fb9}" />
<Item Id="{a06ba470-2048-47ac-b7af-93653ce09359}" /> <Item Id="{a06ba470-2048-47ac-b7af-93653ce09359}" />
<Item Id="{1562ccb9-7f21-492d-8c71-42d5ff7ecf9a}" /> <Item Id="{1562ccb9-7f21-492d-8c71-42d5ff7ecf9a}" />
<Item Id="{34c575e6-55c2-4d7f-a839-8167bfa1a831}" /> <Item Id="{34c575e6-55c2-4d7f-a839-8167bfa1a831}" />
<Item Id="{b253a65c-b13a-42bd-8d59-4a790ce19f3a}" /> <Item Id="{b253a65c-b13a-42bd-8d59-4a790ce19f3a}" />
<Item Id="{011d4fb6-22ba-4d11-8a05-ed753b5dbd8a}" /> <Item Id="{011d4fb6-22ba-4d11-8a05-ed753b5dbd8a}" />
<Item Id="{804634ae-7823-4541-a8fe-d7da6c807b41}" /> <Item Id="{804634ae-7823-4541-a8fe-d7da6c807b41}" />
<Item Id="{eb8a243c-8645-46cf-98ac-e73a6e32cf3e}" /> <Item Id="{eb8a243c-8645-46cf-98ac-e73a6e32cf3e}" />
<Item Id="{5f2e52af-bb7d-46cc-819d-3f2a2860ba8c}" /> <Item Id="{5f2e52af-bb7d-46cc-819d-3f2a2860ba8c}" />
<Item Id="{9848d5d7-d513-4f1e-98aa-fcd21b3eb498}" /> <Item Id="{9848d5d7-d513-4f1e-98aa-fcd21b3eb498}" />
</Bindings> </Bindings>
</Item> </Item>
<Item Id="{66d31aae-e1f0-43b9-8108-123cb59d73bd}" /> <Item Id="{66d31aae-e1f0-43b9-8108-123cb59d73bd}" />
<Item Id="{4fcfb46a-6fd9-4ba4-a790-6447cb1cf0a5}" /> <Item Id="{4fcfb46a-6fd9-4ba4-a790-6447cb1cf0a5}" />
<Item Id="{55d41133-671c-49f1-a934-6c5d59c3f319}"> <Item Id="{55d41133-671c-49f1-a934-6c5d59c3f319}">
<SubFolders> <SubFolders>
<Item Id="{4bc2f87d-6dc0-41b5-9b0b-251498b765df}"> <Item Id="{4bc2f87d-6dc0-41b5-9b0b-251498b765df}">
<Bindings> <Bindings>
<Item Id="{d18a79d1-fff5-4b71-8301-fb8f2397e0ac}" /> <Item Id="{d18a79d1-fff5-4b71-8301-fb8f2397e0ac}" />
<Item Id="{cc238591-6417-4f4c-bca3-08b8ecbb8a1b}"> <Item Id="{cc238591-6417-4f4c-bca3-08b8ecbb8a1b}">
<Enabled>False</Enabled> <Enabled>False</Enabled>
</Item> </Item>
</Bindings> </Bindings>
</Item> </Item>
<Item Id="{0d070b5c-9bc8-4784-9071-548ab0ef93e9}" /> <Item Id="{0d070b5c-9bc8-4784-9071-548ab0ef93e9}" />
</SubFolders> </SubFolders>
</Item> </Item>
<Item Id="{a3f2b3b4-9497-4db5-9f8b-c4b30a2240ac}" /> <Item Id="{a3f2b3b4-9497-4db5-9f8b-c4b30a2240ac}" />
<Item Id="{318b8900-85af-4c66-b37f-8a86508e7a89}" /> <Item Id="{318b8900-85af-4c66-b37f-8a86508e7a89}" />
<Item Id="{d33fb6fa-56b3-40a1-b169-71e855b2e3a2}" /> <Item Id="{d33fb6fa-56b3-40a1-b169-71e855b2e3a2}" />
<Item Id="{4aa0656f-7150-440b-8585-509c548e9d7e}" /> <Item Id="{4aa0656f-7150-440b-8585-509c548e9d7e}" />
</SubFolders> </SubFolders>
</RootFolder> </RootFolder>
</Options> </Options>
</Root> </Root>
@@ -1,93 +1,93 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.Foundation.Templates.TemplatesOptions"> <Root Type="DevExpress.CodeRush.Foundation.Templates.TemplatesOptions">
<Options Language="C#"> <Options Language="C#">
<RootCategory> <RootCategory>
<Categories> <Categories>
<Item Id="{9bc0d39a-b20c-41bb-bd1c-4d08d6d999a9}"> <Item Id="{9bc0d39a-b20c-41bb-bd1c-4d08d6d999a9}">
<Templates> <Templates>
<Item Id="{2b7014a2-6093-4095-ad6d-799b52ef74e0}"> <Item Id="{2b7014a2-6093-4095-ad6d-799b52ef74e0}">
<Items> <Items>
<Item Id="{b3ec38a7-ed71-4ab9-9a43-1623fc3d4c30}"> <Item Id="{b3ec38a7-ed71-4ab9-9a43-1623fc3d4c30}">
<Expansion>//----------------------------------------------------------------------- <Expansion>//-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="«?FileBase»" // file="«?FileBase»"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: «?GetUserInfo(Company)» // AuthorGroup: «?GetUserInfo(Company)»
// Copyright (c) «?GetUserInfo(Company)». All rights reserved. // Copyright (c) «?GetUserInfo(Company)». All rights reserved.
// //
//Permission is hereby granted, free of charge, to any person obtaining a copy //Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal //of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights //in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell //to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is //copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions: //furnished to do so, subject to the following conditions:
// //
//The above copyright notice and this permission notice shall be included in all //The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software. //copies or substantial portions of the Software.
// //
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, //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 //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE. //SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// -----------------------------------------------------------------------</Expansion> // -----------------------------------------------------------------------</Expansion>
</Item> </Item>
</Items> </Items>
</Item> </Item>
<Item Id="{5d7e8673-3c4d-4f8c-98e5-96799d699c6b}" /> <Item Id="{5d7e8673-3c4d-4f8c-98e5-96799d699c6b}" />
<Item Id="{871cff6e-18a7-44e2-9980-cf75ac927e7d}" /> <Item Id="{871cff6e-18a7-44e2-9980-cf75ac927e7d}" />
<Item Id="{fc65f206-ebed-4567-b2ad-5da5fe2d5383}" /> <Item Id="{fc65f206-ebed-4567-b2ad-5da5fe2d5383}" />
<Item Id="{a4ca82b6-2912-47f4-bc3e-fa61fa152c64}" /> <Item Id="{a4ca82b6-2912-47f4-bc3e-fa61fa152c64}" />
<Item Id="{55344716-9e28-4e48-b69f-4ee8c28127cb}" /> <Item Id="{55344716-9e28-4e48-b69f-4ee8c28127cb}" />
<Item Id="{cf975a83-9d8e-420e-a655-27d52cf13c09}" /> <Item Id="{cf975a83-9d8e-420e-a655-27d52cf13c09}" />
<Item Id="{83aac52b-9b01-47e7-932a-8b56e8b86b10}" /> <Item Id="{83aac52b-9b01-47e7-932a-8b56e8b86b10}" />
<Item Id="{dc9f70e9-7053-45ed-90a6-e8257db37251}" /> <Item Id="{dc9f70e9-7053-45ed-90a6-e8257db37251}" />
<Item Id="{bfc86380-a990-47cc-9262-d6d644337e35}" /> <Item Id="{bfc86380-a990-47cc-9262-d6d644337e35}" />
<Item Id="{2830b620-b907-4390-8eda-c030402a9968}" /> <Item Id="{2830b620-b907-4390-8eda-c030402a9968}" />
<Item Id="{efc78e7d-fbe7-4a4f-a489-3ff6b881442b}" /> <Item Id="{efc78e7d-fbe7-4a4f-a489-3ff6b881442b}" />
<Item Id="{336f73d0-1064-4ced-99a4-a36400438379}" /> <Item Id="{336f73d0-1064-4ced-99a4-a36400438379}" />
<Item Id="{5babdc02-57e8-4854-82f7-9a9b97e48dad}" /> <Item Id="{5babdc02-57e8-4854-82f7-9a9b97e48dad}" />
<Item Id="{8f6f1bb6-a38a-4c81-ae9b-f77a8709a73d}" /> <Item Id="{8f6f1bb6-a38a-4c81-ae9b-f77a8709a73d}" />
<Item Id="{b0fb5608-05db-47c4-94b4-2bc7f72bc642}" /> <Item Id="{b0fb5608-05db-47c4-94b4-2bc7f72bc642}" />
<Item Id="{a43c547a-2d44-46eb-b39b-d39f7eb65a24}" /> <Item Id="{a43c547a-2d44-46eb-b39b-d39f7eb65a24}" />
<Item Id="{890dc8c3-dd34-489e-996a-4f3651f3e72a}" /> <Item Id="{890dc8c3-dd34-489e-996a-4f3651f3e72a}" />
<Item Id="{d420e166-39c2-4ea1-a7b2-36416f33de4f}" /> <Item Id="{d420e166-39c2-4ea1-a7b2-36416f33de4f}" />
<Item Id="{e4028d8a-d830-4882-b3ac-88e9913d996c}" /> <Item Id="{e4028d8a-d830-4882-b3ac-88e9913d996c}" />
<Item Id="{ed44f8f7-61d8-4932-ac14-6fb30ff55efb}" /> <Item Id="{ed44f8f7-61d8-4932-ac14-6fb30ff55efb}" />
<Item Id="{d7b9b3af-94a6-4c1b-92c8-8058d1d71f0e}" /> <Item Id="{d7b9b3af-94a6-4c1b-92c8-8058d1d71f0e}" />
<Item Id="{e91be916-8c04-4ebd-a9af-ddc38a046272}" /> <Item Id="{e91be916-8c04-4ebd-a9af-ddc38a046272}" />
<Item Id="{65b10956-7ae0-44ed-a376-6195f7e4d953}" /> <Item Id="{65b10956-7ae0-44ed-a376-6195f7e4d953}" />
</Templates> </Templates>
</Item> </Item>
<Item Id="{8e4132de-c894-486f-8004-e2d08d0ba979}" /> <Item Id="{8e4132de-c894-486f-8004-e2d08d0ba979}" />
<Item Id="{2433228d-3ae2-4022-aa52-7ab7a1a3116b}" /> <Item Id="{2433228d-3ae2-4022-aa52-7ab7a1a3116b}" />
<Item Id="{1379a9c1-e7a0-4872-87e5-24ef7c4a1315}" /> <Item Id="{1379a9c1-e7a0-4872-87e5-24ef7c4a1315}" />
<Item Id="{80783537-1b32-4a51-b716-b32036574f4f}" /> <Item Id="{80783537-1b32-4a51-b716-b32036574f4f}" />
<Item Id="{6b1c63bd-043f-4639-b757-a0c6132270fa}" /> <Item Id="{6b1c63bd-043f-4639-b757-a0c6132270fa}" />
<Item Id="{2ff95045-9084-4395-93a8-70d11cc8cdeb}" /> <Item Id="{2ff95045-9084-4395-93a8-70d11cc8cdeb}" />
<Item Id="{633d268e-6d35-46c7-ac5d-d2885ca35a0d}" /> <Item Id="{633d268e-6d35-46c7-ac5d-d2885ca35a0d}" />
<Item Id="{34b6c10a-ee60-4c30-9f5c-e0c033ce7867}" /> <Item Id="{34b6c10a-ee60-4c30-9f5c-e0c033ce7867}" />
<Item Id="{e63d4dbd-49ce-4fd6-ab9d-99e0bfc60b35}" /> <Item Id="{e63d4dbd-49ce-4fd6-ab9d-99e0bfc60b35}" />
<Item Id="{f4703284-a160-4272-be22-3366ade5e43f}" /> <Item Id="{f4703284-a160-4272-be22-3366ade5e43f}" />
<Item Id="{51f7a921-200c-43e3-944c-54521ed0c201}" /> <Item Id="{51f7a921-200c-43e3-944c-54521ed0c201}" />
<Item Id="{08077498-5ebe-4300-ad58-bcbf5a3ebac3}" /> <Item Id="{08077498-5ebe-4300-ad58-bcbf5a3ebac3}" />
<Item Id="{90ee1142-998a-40f3-8aa1-fe850f766551}" /> <Item Id="{90ee1142-998a-40f3-8aa1-fe850f766551}" />
<Item Id="{662a95d0-4d80-484b-b649-044f1464ff4a}" /> <Item Id="{662a95d0-4d80-484b-b649-044f1464ff4a}" />
<Item Id="{9c9bbf9d-c82c-450d-bafa-2d8b1f6a4a63}" /> <Item Id="{9c9bbf9d-c82c-450d-bafa-2d8b1f6a4a63}" />
<Item Id="{78fc30d9-aa85-4d78-a3f0-8eeb5a2e1496}" /> <Item Id="{78fc30d9-aa85-4d78-a3f0-8eeb5a2e1496}" />
<Item Id="{cb08e1a8-1829-4409-87e1-d9a9d3eea8a3}" /> <Item Id="{cb08e1a8-1829-4409-87e1-d9a9d3eea8a3}" />
<Item Id="{96418b0c-83d3-4656-90c9-59d4139e045c}" /> <Item Id="{96418b0c-83d3-4656-90c9-59d4139e045c}" />
<Item Id="{447c50a1-8ec8-44ee-9915-7f0548350093}" /> <Item Id="{447c50a1-8ec8-44ee-9915-7f0548350093}" />
<Item Id="{a641e756-107f-4e82-9952-010819e35abe}" /> <Item Id="{a641e756-107f-4e82-9952-010819e35abe}" />
<Item Id="{4cada8e9-90e0-4fe3-8171-91a3735d1447}" /> <Item Id="{4cada8e9-90e0-4fe3-8171-91a3735d1447}" />
<Item Id="{3888be66-16ab-4a3e-b416-144246345fa0}" /> <Item Id="{3888be66-16ab-4a3e-b416-144246345fa0}" />
<Item Id="{0158dfdd-6429-462c-931f-1a37959d3b42}" /> <Item Id="{0158dfdd-6429-462c-931f-1a37959d3b42}" />
<Item Id="{bf0539d1-191e-4764-b583-c95277cfa4bf}" /> <Item Id="{bf0539d1-191e-4764-b583-c95277cfa4bf}" />
</Categories> </Categories>
</RootCategory> </RootCategory>
</Options> </Options>
</Root> </Root>
+31 -31
View File
@@ -1,32 +1,32 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.Formatting.UI.Options.SpacesOptionsContainer"> <Root Type="DevExpress.CodeRush.Formatting.UI.Options.SpacesOptionsContainer">
<Options Language="C#"> <Options Language="C#">
<Options> <Options>
<Item Id="{a4f7d021-5762-49a9-956e-040794d03b98}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.BinaryExpressionSpacesOption" /> <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="{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="{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="{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="{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="{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="{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="{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="{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="{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="{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="{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="{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="{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="{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="{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="{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="{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="{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="{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="{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="{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"> <Item Id="{7a5f4d31-4fc4-4c34-ba85-2baad09873b4}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CollectionExpressionSpacesOption">
<WithinBrackets>False</WithinBrackets> <WithinBrackets>False</WithinBrackets>
</Item> </Item>
</Options> </Options>
</Options> </Options>
</Root> </Root>
+37 -37
View File
@@ -1,38 +1,38 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.Formatting.UI.Options.WrappingOptionsContainer"> <Root Type="DevExpress.CodeRush.Formatting.UI.Options.WrappingOptionsContainer">
<Options Language="C#"> <Options Language="C#">
<Options> <Options>
<Item Id="{48f3f6c0-b3c2-4567-b071-33af7480c9a3}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.QueryExpressionFormattingOption" /> <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="{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="{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="{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="{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="{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="{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"> <Item Id="{25746bdc-0a43-4741-a740-20784691314b}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.InvocationArgumentListFormattingOption">
<WrapOpenParen> <WrapOpenParen>
<Option>NoWrap</Option> <Option>NoWrap</Option>
</WrapOpenParen> </WrapOpenParen>
<WrapArgSeparator> <WrapArgSeparator>
<Option>SimpleWrap</Option> <Option>SimpleWrap</Option>
<Kind>WrapAfter</Kind> <Kind>WrapAfter</Kind>
</WrapArgSeparator> </WrapArgSeparator>
<ArgCountTolerance>4</ArgCountTolerance> <ArgCountTolerance>4</ArgCountTolerance>
</Item> </Item>
<Item Id="{D5EE2ECB-9FA6-47F7-A11F-007FC45E872D}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.AttributeArgumentsFormattingOption" /> <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="{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="{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="{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="{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="{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="{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="{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="{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="{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="{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="{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="{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" /> <Item Id="{099d9359-1af3-40a2-8841-50f6cc9527e1}" Type="DevExpress.CodeRush.Formatting.UI.Options.Conversion.CollectionExpressionWrappingFormattingOption" />
</Options> </Options>
</Options> </Options>
</Root> </Root>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.VisualizePlugins.RightMarginLine.Options.RightMarginLineOptions"> <Root Type="DevExpress.CodeRush.VisualizePlugins.RightMarginLine.Options.RightMarginLineOptions">
<Options Language="Neutral"> <Options Language="Neutral">
<Column>120</Column> <Column>120</Column>
</Options> </Options>
</Root> </Root>
+125 -125
View File
@@ -1,126 +1,126 @@
[*] [*]
end_of_line = lf end_of_line = lf
charset = utf-8 charset = utf-8
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
[*.{bat,cmd,ps1}] [*.{bat,cmd,ps1}]
end_of_line = crlf end_of_line = crlf
[*.cs] [*.cs]
# CS1591: 缺少对公共可见类型或成员的 XML 注释 # CS1591: 缺少对公共可见类型或成员的 XML 注释
dotnet_diagnostic.CS1591.severity = suggestion dotnet_diagnostic.CS1591.severity = suggestion
[*.cs] [*.cs]
#### 命名样式 #### #### 命名样式 ####
# 命名规则 # 命名规则
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion 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.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i 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.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case 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.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.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case 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_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers = dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum 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.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers = dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method 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.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers = dotnet_naming_symbols.non_field_members.required_modifiers =
# 命名样式 # 命名样式
dotnet_naming_style.begins_with_i.required_prefix = I dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix = dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator = dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix = dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix = dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix = dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix = dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case dotnet_naming_style.pascal_case.capitalization = pascal_case
csharp_using_directive_placement = outside_namespace:silent csharp_using_directive_placement = outside_namespace:silent
csharp_style_expression_bodied_methods = false:silent csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_lambdas = true:silent csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent csharp_style_expression_bodied_local_functions = false:silent
csharp_style_conditional_delegate_call = true:suggestion csharp_style_conditional_delegate_call = true:suggestion
csharp_style_var_for_built_in_types = false:silent csharp_style_var_for_built_in_types = false:silent
csharp_style_var_when_type_is_apparent = false:silent csharp_style_var_when_type_is_apparent = false:silent
csharp_style_var_elsewhere = false:silent csharp_style_var_elsewhere = false:silent
csharp_prefer_simple_using_statement = false:suggestion csharp_prefer_simple_using_statement = false:suggestion
csharp_prefer_braces = true:silent csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = block_scoped:silent csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = false:silent csharp_style_prefer_top_level_statements = false:silent
csharp_style_prefer_primary_constructors = true:suggestion csharp_style_prefer_primary_constructors = true:suggestion
csharp_prefer_static_local_function = true:suggestion csharp_prefer_static_local_function = true:suggestion
csharp_prefer_static_anonymous_function = true:suggestion csharp_prefer_static_anonymous_function = true:suggestion
csharp_style_prefer_readonly_struct = true:suggestion csharp_style_prefer_readonly_struct = true:suggestion
csharp_space_around_binary_operators = before_and_after csharp_space_around_binary_operators = before_and_after
csharp_indent_labels = one_less_than_current csharp_indent_labels = one_less_than_current
[*.vb] [*.vb]
#### 命名样式 #### #### 命名样式 ####
# 命名规则 # 命名规则
dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion 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_开始.symbols = interface
dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始 dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型 dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员 dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
# 符号规范 # 符号规范
dotnet_naming_symbols.interface.applicable_kinds = interface 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.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.interface.required_modifiers = dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum 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.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.类型.required_modifiers = dotnet_naming_symbols.类型.required_modifiers =
dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method
dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.非字段成员.required_modifiers = dotnet_naming_symbols.非字段成员.required_modifiers =
# 命名样式 # 命名样式
dotnet_naming_style.以_i_开始.required_prefix = I dotnet_naming_style.以_i_开始.required_prefix = I
dotnet_naming_style.以_i_开始.required_suffix = dotnet_naming_style.以_i_开始.required_suffix =
dotnet_naming_style.以_i_开始.word_separator = dotnet_naming_style.以_i_开始.word_separator =
dotnet_naming_style.以_i_开始.capitalization = pascal_case dotnet_naming_style.以_i_开始.capitalization = pascal_case
dotnet_naming_style.帕斯卡拼写法.required_prefix = dotnet_naming_style.帕斯卡拼写法.required_prefix =
dotenv 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 /build
/.idea /.idea
/.vs /.vs
/.vscode /.vscode
*/obj */obj
*/bin */bin
*/build */build
*/Debug */Debug
*/Release */Release
/TestResults /TestResults
*/x64 */x64
*/.vs */.vs
/StgSharp.Native/lib /StgSharp.Native/lib
obj obj
+37 -37
View File
@@ -1,37 +1,37 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.14.36705.20 VisualStudioVersion = 17.14.36705.20
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenerateGL", "StgSharp.GenerateGL\GenerateGL.csproj", "{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenerateGL", "StgSharp.GenerateGL\GenerateGL.csproj", "{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StgSharp.Common", "StgSharp.Common\StgSharp.Common.csproj", "{5D646C0E-CCF8-B5B4-7DE8-1C37542E7B4D}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StgSharp.Common", "StgSharp.Common\StgSharp.Common.csproj", "{5D646C0E-CCF8-B5B4-7DE8-1C37542E7B4D}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64 Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64 Release|x64 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {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|Any CPU.Build.0 = Debug|Any CPU
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Debug|x64.ActiveCfg = Debug|x64 {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}.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.ActiveCfg = Release|Any CPU
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Release|Any CPU.Build.0 = 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.ActiveCfg = Release|x64
{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}.Release|x64.Build.0 = 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|Any CPU.ActiveCfg = Debug|x64
{5D646C0E-CCF8-B5B4-7DE8-1C37542E7B4D}.Debug|x64.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|Any CPU.ActiveCfg = Release|x64
{5D646C0E-CCF8-B5B4-7DE8-1C37542E7B4D}.Release|x64.ActiveCfg = Release|x64 {5D646C0E-CCF8-B5B4-7DE8-1C37542E7B4D}.Release|x64.ActiveCfg = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AC71C01F-9867-4E43-85AB-4A8A7896B1BC} SolutionGuid = {AC71C01F-9867-4E43-85AB-4A8A7896B1BC}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal
+155 -155
View File
@@ -1,155 +1,155 @@
MIT License MIT License
Copyright (c) 2024 Nitload StgSharp Team Copyright (c) 2024 Nitload StgSharp Team
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
StgSharp program uses the following open source projects, StgSharp program uses the following open source projects,
I'd like to refer to them here and show my sincere appreciation, I'd like to refer to them here and show my sincere appreciation,
while the names are sorted in no particular order while the names are sorted in no particular order
1. GLFW 1. GLFW
Remote repository on: https://www.glfw.org/ Remote repository on: https://www.glfw.org/
License of GLFW: License of GLFW:
Copyright (c) 2002-2006 Marcus Geelnard Copyright (c) 2002-2006 Marcus Geelnard
Copyright (c) 2006-2019 Camilla Löwy Copyright (c) 2006-2019 Camilla Löwy
This software is provided 'as-is', This software is provided 'as-is',
without any express or implied warranty. without any express or implied warranty.
In no event will the authors be held liable for In no event will the authors be held liable for
any damages arising from the use of this software. any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely, including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions: subject to the following restrictions:
The origin of this software must not be misrepresented; The origin of this software must not be misrepresented;
you must not claim that you wrote the original software. you must not claim that you wrote the original software.
If you use this software in a product, an acknowledgment If you use this software in a product, an acknowledgment
in the product documentation would be appreciated but is not required. in the product documentation would be appreciated but is not required.
Altered source versions must be plainly marked as such, Altered source versions must be plainly marked as such,
and must not be misrepresented as being the original software. and must not be misrepresented as being the original software.
This notice may not be removed or altered from any source distribution. This notice may not be removed or altered from any source distribution.
2. GLAD, Khronos Specifications and other Specification headers 2. GLAD, Khronos Specifications and other Specification headers
Web service available on https://gen.glad.sh/ Web service available on https://gen.glad.sh/
The glad source code: The glad source code:
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2013-2022 David Herberth Copyright (c) 2013-2022 David Herberth
Permission is hereby granted, free of charge, to any person obtaining a copy of 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 this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 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, the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 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 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 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. CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
The Khronos Specifications: The Khronos Specifications:
Copyright (c) 2013-2020 The Khronos Group Inc. Copyright (c) 2013-2020 The Khronos Group Inc.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
3. docs.gl 3. docs.gl
4. Terminal.GUI 4. Terminal.GUI
Remote repository on: https://github.com/gui-cs/Terminal.Gui Remote repository on: https://github.com/gui-cs/Terminal.Gui
License of Terminal.GUI: License of Terminal.GUI:
Copyright 2007-2011 Novell Inc Copyright 2007-2011 Novell Inc
Copyright 2017 Microsoft Corp Copyright 2017 Microsoft Corp
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software. included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
5. IxMilia.Step 5. IxMilia.Step
License of IxMilia.Step: License of IxMilia.Step:
The MIT License (MIT) The MIT License (MIT)
Copyright (c) IxMilia. Copyright (c) IxMilia.
All rights reserved. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
+239 -239
View File
@@ -1,240 +1,240 @@
# StgSharp # StgSharp
![StgSharp Logo](STG%23LOGO.png) ![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. 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 ## 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. 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 ## Core Modules
### StgSharp.Common ### StgSharp.Common
The foundational library containing shared functionality: The foundational library containing shared functionality:
- **Mathematics**: High-performance linear algebra, matrix operations, and mathematical utilities - **Mathematics**: High-performance linear algebra, matrix operations, and mathematical utilities
- **Graphics**: OpenGL bindings, rendering pipeline, and graphics framework - **Graphics**: OpenGL bindings, rendering pipeline, and graphics framework
- **High Performance**: SIMD operations, memory management, and parallel computing - **High Performance**: SIMD operations, memory management, and parallel computing
- **Collections**: Specialized data structures and containers - **Collections**: Specialized data structures and containers
- **MVVM**: Model-View-ViewModel framework for UI applications - **MVVM**: Model-View-ViewModel framework for UI applications
- **Entities**: Game entity system and behavior management - **Entities**: Game entity system and behavior management
- **Environment**: Platform abstraction and system integration - **Environment**: Platform abstraction and system integration
### StgSharp.Mathematics (Numeric) ### StgSharp.Mathematics (Numeric)
Numerics and linear algebra built on top of the core library: Numerics and linear algebra built on top of the core library:
- **Matrix & Vector Types**: High-performance math primitives - **Matrix & Vector Types**: High-performance math primitives
- **Numeric Utilities**: Core numeric helpers and algorithms - **Numeric Utilities**: Core numeric helpers and algorithms
- **SIMD-Friendly Kernels**: Data layouts optimized for hardware acceleration - **SIMD-Friendly Kernels**: Data layouts optimized for hardware acceleration
### StgSharp.RegularAnalysis ### StgSharp.RegularAnalysis
Regular-language and parsing infrastructure (replaces the former Script module): Regular-language and parsing infrastructure (replaces the former Script module):
- **Regex/Automata Core**: Regular expression processing and automata utilities - **Regex/Automata Core**: Regular expression processing and automata utilities
- **Parsing Primitives**: Reusable tokenizer and grammar building blocks - **Parsing Primitives**: Reusable tokenizer and grammar building blocks
- **Analysis Pipeline**: Structured processing for language-like inputs - **Analysis Pipeline**: Structured processing for language-like inputs
### StgSharp.Native ### StgSharp.Native
Native library integration: Native library integration:
- **GLFW Integration**: Cross-platform window and input management - **GLFW Integration**: Cross-platform window and input management
- **SIMD Operations**: Hardware-accelerated vector operations - **SIMD Operations**: Hardware-accelerated vector operations
- **Matrix Kernels**: Optimized matrix computation routines - **Matrix Kernels**: Optimized matrix computation routines
- **Context Management**: Graphics context and resource management - **Context Management**: Graphics context and resource management
### StgSharp.Device ### StgSharp.Device
Low-level device and port abstractions: Low-level device and port abstractions:
- **Command Interfaces**: Command shapes and serialization contracts - **Command Interfaces**: Command shapes and serialization contracts
- **Port Control**: Stream-oriented device access - **Port Control**: Stream-oriented device access
- **Decoder/Response Models**: Protocol-level data handling - **Decoder/Response Models**: Protocol-level data handling
### StgSharp.TerminalDialogue ### StgSharp.TerminalDialogue
Terminal-based user interface application and components: Terminal-based user interface application and components:
- **Interactive Terminal**: Command-line UI components - **Interactive Terminal**: Command-line UI components
- **Dialogue System**: User interaction and input handling - **Dialogue System**: User interaction and input handling
- **Terminal Graphics**: Text-based UI rendering - **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. > 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 ## Key Features
### High-Performance Computing ### High-Performance Computing
- **Parallel Matrix Operations**: Multi-threaded matrix computations with optimized scheduling - **Parallel Matrix Operations**: Multi-threaded matrix computations with optimized scheduling
- **SIMD Acceleration**: Hardware-optimized vector operations - **SIMD Acceleration**: Hardware-optimized vector operations
- **Memory Management**: Custom allocators including HLSF (Hybrid Layer Segregated Fit) - **Memory Management**: Custom allocators including HLSF (Hybrid Layer Segregated Fit)
- **Thread Pool Management**: Efficient task scheduling and execution - **Thread Pool Management**: Efficient task scheduling and execution
#### HLSF Benchmark Snapshot (BenchmarkDotNet) #### HLSF Benchmark Snapshot (BenchmarkDotNet)
``` ```
BenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.7623) 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 AMD Ryzen 7 6800H with Radeon Graphics, 1 CPU, 16 logical and 8 physical cores
.NET SDK 10.0.102 .NET SDK 10.0.102
[Host] : .NET 8.0.23 (8.0.2325.60607), X64 RyuJIT AVX2 [Host] : .NET 8.0.23 (8.0.2325.60607), X64 RyuJIT AVX2
Toolchain=InProcessEmitToolchain InvocationCount=2048 UnrollFactor=1 Toolchain=InProcessEmitToolchain InvocationCount=2048 UnrollFactor=1
``` ```
| Method | Mean | Error | StdDev | Ratio | RatioSD | | Method | Mean | Error | StdDev | Ratio | RatioSD |
|------- |-----:|-----:|------:|------:|-------:| |------- |-----:|-----:|------:|------:|-------:|
| Hlsf | 76.23 µs | 0.355 µs | 0.315 µs | 0.44 | 0.01 | | 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 | | 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), This benchmark measures steady-state small-block allocation throughput (64B–4KB),
which represents the dominant allocation pattern in StgSharp's matrix computation pipeline. 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. 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. Under this workload, HLSF achieves ~2.3× throughput over system malloc. This is not a general-purpose allocator comparison.
### Graphics and Rendering ### Graphics and Rendering
- **OpenGL Integration**: Complete OpenGL API coverage - **OpenGL Integration**: Complete OpenGL API coverage
- **Cross-Platform Support**: Windows and Linux compatibility - **Cross-Platform Support**: Windows and Linux compatibility
- **Rendering Pipeline**: Flexible and extensible graphics framework - **Rendering Pipeline**: Flexible and extensible graphics framework
- **Shader Management**: Dynamic shader compilation and management - **Shader Management**: Dynamic shader compilation and management
### Game Engine Capabilities ### Game Engine Capabilities
- **Entity System**: Flexible game object management - **Entity System**: Flexible game object management
- **State Machine**: Game state and behavior management - **State Machine**: Game state and behavior management
- **Input Handling**: Comprehensive input and control systems - **Input Handling**: Comprehensive input and control systems
- **Resource Management**: Efficient asset loading and management - **Resource Management**: Efficient asset loading and management
### Data Processing ### Data Processing
- **STEP File Support**: Industrial data format processing - **STEP File Support**: Industrial data format processing
- **EXPRESS Language**: Domain-specific language compilation - **EXPRESS Language**: Domain-specific language compilation
- **Pipeline Processing**: Efficient data transformation workflows - **Pipeline Processing**: Efficient data transformation workflows
## Requirements ## Requirements
- .NET 8.0 or later - .NET 8.0 or later
- Windows 10/11 or Linux - Windows 10/11 or Linux
- x64 or ARM64 architecture - x64 or ARM64 architecture
- OpenGL 3.3+ support - OpenGL 3.3+ support
## Installation ## 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. 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 ### Prerequisites
- **Windows**: Visual Studio 2022 or later with C++ development tools - **Windows**: Visual Studio 2022 or later with C++ development tools
- **Linux**: CMake and compatible C++ compiler (planned support) - **Linux**: CMake and compatible C++ compiler (planned support)
- .NET 8.0 SDK or later - .NET 8.0 SDK or later
- Git for source code retrieval - Git for source code retrieval
### Building from Source ### Building from Source
1. **Clone the repository**: 1. **Clone the repository**:
```bash ```bash
git clone https://github.com/Nitload-NSI/StgSharp.git git clone https://github.com/Nitload-NSI/StgSharp.git
cd StgSharp cd StgSharp
``` ```
2. **Windows (Recommended)**: 2. **Windows (Recommended)**:
- Open `StgSharp.sln` in Visual Studio - Open `StgSharp.sln` in Visual Studio
- Build the solution (Debug or Release configuration) - Build the solution (Debug or Release configuration)
- The native components will be automatically compiled - The native components will be automatically compiled
3. **Linux (Experimental)**: 3. **Linux (Experimental)**:
```bash ```bash
# Build .NET components only # Build .NET components only
# Note: Native components are not yet supported on Linux # Note: Native components are not yet supported on Linux
dotnet build dotnet build
``` ```
### Build Configuration ### Build Configuration
- **Debug**: Full debugging symbols, unoptimized - **Debug**: Full debugging symbols, unoptimized
- **Release**: Optimized for performance - **Release**: Optimized for performance
### Notes ### Notes
- Windows compilation with Visual Studio is fully supported and tested - Windows compilation with Visual Studio is fully supported and tested
- Linux support is experimental and limited to .NET components only - Linux support is experimental and limited to .NET components only
- Native components (StgSharp.Native) are currently Windows-only and require MSVC - Native components (StgSharp.Native) are currently Windows-only and require MSVC
- CMake support for native components is not yet implemented - CMake support for native components is not yet implemented
- Some features may not be available on all platforms - Some features may not be available on all platforms
## Quick Start ## Quick Start
### Basic Graphics Application ### Basic Graphics Application
```csharp ```csharp
/*To be implemented*/ /*To be implemented*/
``` ```
### Matrix Operations ### Matrix Operations
```csharp ```csharp
using StgSharp.Mathematics; using StgSharp.Mathematics;
// Create matrices // Create matrices
var matrixA = Matrix<float>.Create(4, 4); var matrixA = Matrix<float>.Create(4, 4);
var matrixB = Matrix<float>.Create(4, 4); var matrixB = Matrix<float>.Create(4, 4);
// Perform parallel matrix multiplication // Perform parallel matrix multiplication
var result = matrixA * matrixB; var result = matrixA * matrixB;
``` ```
### Memory Management ### Memory Management
```csharp ```csharp
using StgSharp.HighPerformance.Memory; using StgSharp.HighPerformance.Memory;
// Create high-performance allocator // Create high-performance allocator
using var allocator = new HybridLayerSegregatedFitAllocator(64 * 1024 * 1024); using var allocator = new HybridLayerSegregatedFitAllocator(64 * 1024 * 1024);
// Allocate memory // Allocate memory
var handle = allocator.Alloc(1024); var handle = allocator.Alloc(1024);
// Use allocated memory // Use allocated memory
allocator.Free(handle); allocator.Free(handle);
``` ```
## Documentation ## Documentation
### Technical Documentation ### Technical Documentation
- [HLSF Allocator](StgSharp.Common/HighPerformance/Memory/InroductionToHLSF.md) - High-performance memory allocator 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 - [Native Library Naming](StgSharp.Native/naming.md) - Native library file naming conventions
## Contributing ## Contributing
StgSharp is developed by Nitload Space. Contributions are welcome, particularly in: StgSharp is developed by Nitload Space. Contributions are welcome, particularly in:
- Performance optimizations - Performance optimizations
- Cross-platform compatibility - Cross-platform compatibility
- Documentation improvements - Documentation improvements
- New feature implementations - New feature implementations
## License ## License
StgSharp is licensed under the MIT License. See [LICENSE](LICENSE) for details. StgSharp is licensed under the MIT License. See [LICENSE](LICENSE) for details.
## Future Plans ## Future Plans
- **Vulkan Support**: Planned for version 2.0 - **Vulkan Support**: Planned for version 2.0
- **Enhanced Cross-Platform**: Improved Linux and mobile support - **Enhanced Cross-Platform**: Improved Linux and mobile support
- **Performance Optimizations**: Further SIMD and parallel processing improvements - **Performance Optimizations**: Further SIMD and parallel processing improvements
- **Extended Language Support**: Additional domain-specific languages - **Extended Language Support**: Additional domain-specific languages
## Support ## Support
For questions, issues, or contributions, please refer to the project repository or contact the development team. 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"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<LangVersion>preview</LangVersion> <LangVersion>preview</LangVersion>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject> <IsTestProject>true</IsTestProject>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" /> <PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.MSTest" Version="1.1.2" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.MSTest" Version="1.1.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" /> <PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" /> <PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\StgSharp.Common\StgSharp.Common.csproj" /> <ProjectReference Include="..\StgSharp.Common\StgSharp.Common.csproj" />
<ProjectReference Include="..\StgSharp.Model\StgSharp.Model.csproj" /> <ProjectReference Include="..\StgSharp.Model\StgSharp.Model.csproj" />
<ProjectReference Include="..\StgSharp.Script\StgSharp.Script.csproj" /> <ProjectReference Include="..\StgSharp.Script\StgSharp.Script.csproj" />
<ProjectReference Include="..\StgSharp.TerminalDialogue\StgSharp.TerminalDialogue.csproj" /> <ProjectReference Include="..\StgSharp.TerminalDialogue\StgSharp.TerminalDialogue.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" /> <Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="xcopy /E /I /Y $(SolutionDir)build $(OutDir)" /> <Exec Command="xcopy /E /I /Y $(SolutionDir)build $(OutDir)" />
</Target> </Target>
</Project> </Project>
+117 -117
View File
@@ -1,118 +1,118 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// file="UnitTest1.cs" // file="UnitTest1.cs"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person // Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation // obtaining a copy of this software and associated documentation
// files (the ¡°Software¡±), to deal in the Software without restriction, // files (the ¡°Software¡±), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, // including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, // publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so, // and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions: // subject to the following conditions:
// //
// The above copyright notice and // The above copyright notice and
// this permission notice shall be included in all copies // this permission notice shall be included in all copies
// or substantial portions of the Software. // or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED ¡°AS IS¡±, // THE SOFTWARE IS PROVIDED ¡°AS IS¡±,
// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // 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, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH // ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// //
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
using StgSharp; using StgSharp;
using StgSharp.Collections; using StgSharp.Collections;
using StgSharp.Script; using StgSharp.Script;
using StgSharp.Script.Express; using StgSharp.Script.Express;
//using StgSharp.Model; //using StgSharp.Model;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace STgSharpTest namespace STgSharpTest
{ {
[TestClass] [TestClass]
public class UnitTest1 public class UnitTest1
{ {
string testExpPath = @"D:\Programmer\stepcode\data\ap203\ap203.exp"; string testExpPath = @"D:\Programmer\stepcode\data\ap203\ap203.exp";
[TestMethod] [TestMethod]
public void RegexTest() public void RegexTest()
{ {
string sample = "FILE_DESCRIPTION(('CATIA V5 STEP Exchange'),'2;1');"; string sample = "FILE_DESCRIPTION(('CATIA V5 STEP Exchange'),'2;1');";
foreach( var match in Regex.Match( foreach( var match in Regex.Match(
sample, @"^(?<CALLER>[A-Z_]+)\s?\((?<PARAM>.+)\)\;" ) sample, @"^(?<CALLER>[A-Z_]+)\s?\((?<PARAM>.+)\)\;" )
.Groups ) { .Groups ) {
Console.WriteLine( $"{match.ToString()}" ); Console.WriteLine( $"{match.ToString()}" );
Console.WriteLine( "\t\t\t" ); Console.WriteLine( "\t\t\t" );
} }
} }
[TestMethod] [TestMethod]
public void TestSource() public void TestSource()
{ {
ScriptSourceTransmitter st = new ScriptSourceTransmitter("end", 0); ScriptSourceTransmitter st = new ScriptSourceTransmitter("end", 0);
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
st.WriteLine($"Code{i} {new string(' ', i + 1)}-- Comment{i}"); st.WriteLine($"Code{i} {new string(' ', i + 1)}-- Comment{i}");
} }
st.WriteLine("Code (* comment"); st.WriteLine("Code (* comment");
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
st.WriteLine($"comment-code"); st.WriteLine($"comment-code");
} }
st.WriteLine($"comment *) code"); st.WriteLine($"comment *) code");
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
st.WriteLine($" Code{i} (* (* Comment *) *) Code{i}"); st.WriteLine($" Code{i} (* (* Comment *) *) Code{i}");
} }
st.WriteLine("end"); st.WriteLine("end");
ExpSourceReader reader = new ExpSourceReader(st); ExpSourceReader reader = new ExpSourceReader(st);
while (!st.IsEmpty) while (!st.IsEmpty)
{ {
Console.WriteLine(new string(reader.ReadLine())); Console.WriteLine(new string(reader.ReadLine()));
} }
} }
[TestMethod] [TestMethod]
public void TestMethod() public void TestMethod()
{ {
ScriptSourceTransmitter st = new ScriptSourceTransmitter("end", 0); ScriptSourceTransmitter st = new ScriptSourceTransmitter("end", 0);
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
st.WriteLine($"Code{i} {new string(' ',i+1)}-- Comment{i}"); st.WriteLine($"Code{i} {new string(' ',i+1)}-- Comment{i}");
} }
st.WriteLine("Code (* comment"); st.WriteLine("Code (* comment");
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
st.WriteLine($"comment-code"); st.WriteLine($"comment-code");
} }
st.WriteLine($"comment *) code"); st.WriteLine($"comment *) code");
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
st.WriteLine($" Code{i} (* (* Comment *) *) Code{i}"); st.WriteLine($" Code{i} (* (* Comment *) *) Code{i}");
} }
st.WriteLine("end"); st.WriteLine("end");
ExpSourceReader reader = new ExpSourceReader(st); ExpSourceReader reader = new ExpSourceReader(st);
while (!st.IsEmpty) while (!st.IsEmpty)
{ {
Console.WriteLine(reader.ReadToken().Value.ToString()); Console.WriteLine(reader.ReadToken().Value.ToString());
} }
} }
} }
} }
+155 -155
View File
@@ -1,155 +1,155 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="HlsfAllocatorBench" // file="HlsfAllocatorBench"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using StgSharp.Mathematics.Memory; using StgSharp.Mathematics.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace StgSharp.Benchmark namespace StgSharp.Benchmark
{ {
[MemoryDiagnoser] [MemoryDiagnoser]
[InvocationCount(2048)] [InvocationCount(2048)]
public class HlsfAllocatorBench public class HlsfAllocatorBench
{ {
private const uint Alignment = 64; private const uint Alignment = 64;
private const int AllocationCount = 2048; private const int AllocationCount = 2048;
private static readonly int[] AllocSizes = [ private static readonly int[] AllocSizes = [
64, 64,
96, 96,
150, 150,
200, 200,
400, 400,
1024, 1024,
4096 4096
]; ];
private HybridLayerSegregatedFitAllocator _hlsfAllocator = null!; private HybridLayerSegregatedFitAllocator _hlsfAllocator = null!;
private readonly List<HybridLayerSegregatedFitAllocationHandle> _hlsfLocalList = new(); private readonly List<HybridLayerSegregatedFitAllocationHandle> _hlsfLocalList = new();
private readonly List<nint> _libcLocalList = new(); private readonly List<nint> _libcLocalList = new();
private Random _random = new(42); private Random _random = new(42);
[GlobalCleanup] [GlobalCleanup]
public void Cleanup() public void Cleanup()
{ {
_hlsfAllocator?.Dispose(); _hlsfAllocator?.Dispose();
_hlsfLocalList.Clear(); _hlsfLocalList.Clear();
_hlsfLocalList.TrimExcess(); _hlsfLocalList.TrimExcess();
_libcLocalList.Clear(); _libcLocalList.Clear();
_libcLocalList.TrimExcess(); _libcLocalList.TrimExcess();
} }
[Benchmark] [Benchmark]
public void Hlsf() public void Hlsf()
{ {
RunHlsf(_hlsfAllocator, FreePolicy.NoCollect); RunHlsf(_hlsfAllocator, FreePolicy.NoCollect);
} }
[IterationSetup] [IterationSetup]
public void IterationSetup() public void IterationSetup()
{ {
_hlsfLocalList.Clear(); _hlsfLocalList.Clear();
_libcLocalList.Clear(); _libcLocalList.Clear();
} }
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public unsafe void Libc() public unsafe void Libc()
{ {
_libcLocalList.Clear(); _libcLocalList.Clear();
for (int i = 0; i < AllocationCount; i++) for (int i = 0; i < AllocationCount; i++)
{ {
uint size = AlignSize(GetNextRandomSize()); uint size = AlignSize(GetNextRandomSize());
void* allocate = NativeMemory.AlignedAlloc(size, Alignment); void* allocate = NativeMemory.AlignedAlloc(size, Alignment);
if (allocate == null) { if (allocate == null) {
throw new OutOfMemoryException("NativeMemory.AlignedAlloc returned null."); throw new OutOfMemoryException("NativeMemory.AlignedAlloc returned null.");
} }
_libcLocalList.Add((nint)allocate); _libcLocalList.Add((nint)allocate);
} }
for (int i = 0; i < _libcLocalList.Count; i++) { for (int i = 0; i < _libcLocalList.Count; i++) {
NativeMemory.AlignedFree((void*)_libcLocalList[i]); NativeMemory.AlignedFree((void*)_libcLocalList[i]);
} }
} }
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()
{ {
_random = new Random(42); _random = new Random(42);
_hlsfAllocator = new HybridLayerSegregatedFitAllocator(GetArenaBytes(), align:(int)Alignment); _hlsfAllocator = new HybridLayerSegregatedFitAllocator(GetArenaBytes(), align:(int)Alignment);
} }
private static uint AlignSize( private static uint AlignSize(
uint size uint size
) )
{ {
uint mask = Alignment - 1; uint mask = Alignment - 1;
return (size + mask) & ~mask; return (size + mask) & ~mask;
} }
private static nuint GetArenaBytes() private static nuint GetArenaBytes()
{ {
const int defaultMb = 512; const int defaultMb = 512;
const int minMb = 64; const int minMb = 64;
int mb = defaultMb; int mb = defaultMb;
string? env = Environment.GetEnvironmentVariable("HLSF_BENCH_ARENA_MB"); string? env = Environment.GetEnvironmentVariable("HLSF_BENCH_ARENA_MB");
if (!string.IsNullOrWhiteSpace(env) && if (!string.IsNullOrWhiteSpace(env) &&
int.TryParse(env, out int parsed) && int.TryParse(env, out int parsed) &&
parsed >= minMb) { parsed >= minMb) {
mb = parsed; mb = parsed;
} }
return (nuint)mb * 1024u * 1024u; return (nuint)mb * 1024u * 1024u;
} }
private uint GetNextRandomSize() => (uint)AllocSizes[_random.Next(AllocSizes.Length)]; private uint GetNextRandomSize() => (uint)AllocSizes[_random.Next(AllocSizes.Length)];
private void RunHlsf( private void RunHlsf(
HybridLayerSegregatedFitAllocator allocator, HybridLayerSegregatedFitAllocator allocator,
FreePolicy policy FreePolicy policy
) )
{ {
_hlsfLocalList.Clear(); _hlsfLocalList.Clear();
for (int i = 0; i < AllocationCount; i++) for (int i = 0; i < AllocationCount; i++)
{ {
uint size = AlignSize(GetNextRandomSize()); uint size = AlignSize(GetNextRandomSize());
HybridLayerSegregatedFitAllocationHandle allocate = allocator.Alloc(size); HybridLayerSegregatedFitAllocationHandle allocate = allocator.Alloc(size);
_hlsfLocalList.Add(allocate); _hlsfLocalList.Add(allocate);
} }
for (int i = 0; i < _hlsfLocalList.Count; i++) { for (int i = 0; i < _hlsfLocalList.Count; i++) {
allocator.Free(_hlsfLocalList[i], policy); allocator.Free(_hlsfLocalList[i], policy);
} }
} }
} }
} }
+52 -52
View File
@@ -1,52 +1,52 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="Program" // file="Program"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using BenchmarkDotNet.Configs; using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Running; using BenchmarkDotNet.Running;
using BenchmarkDotNet.Toolchains.InProcess.Emit; using BenchmarkDotNet.Toolchains.InProcess.Emit;
using StgSharp.Benchmark; using StgSharp.Benchmark;
namespace StgSharp.Benchmark namespace StgSharp.Benchmark
{ {
internal static class Program internal static class Program
{ {
private static void Main( private static void Main(
string[] args string[] args
) )
{ {
IConfig config = ManualConfig.Create(DefaultConfig.Instance) IConfig config = ManualConfig.Create(DefaultConfig.Instance)
.AddLogger(ConsoleLogger.Default) .AddLogger(ConsoleLogger.Default)
.AddJob(Job.Default .AddJob(Job.Default
.WithToolchain(InProcessEmitToolchain.Instance)); .WithToolchain(InProcessEmitToolchain.Instance));
_ = BenchmarkRunner.Run<SwissTableBench>(config); _ = BenchmarkRunner.Run<SwissTableBench>(config);
} }
} }
} }
+24 -24
View File
@@ -1,24 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<SolutionDir Condition="'$(SolutionDir)'=='' Or '$(SolutionDir)'=='*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir> <SolutionDir Condition="'$(SolutionDir)'=='' Or '$(SolutionDir)'=='*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" /> <PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\StgSharp.Common\StgSharp.Common.csproj" Properties="DisablePostBuildCopy=true" /> <ProjectReference Include="..\StgSharp.Common\StgSharp.Common.csproj" Properties="DisablePostBuildCopy=true" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<NativeArtifacts Include="$(SolutionDir)build\**\StgSharp.Native*.dll" /> <NativeArtifacts Include="$(SolutionDir)build\**\StgSharp.Native*.dll" />
<NativeArtifacts Include="$(SolutionDir)build\**\StgSharp.Native*.pdb" /> <NativeArtifacts Include="$(SolutionDir)build\**\StgSharp.Native*.pdb" />
</ItemGroup> </ItemGroup>
<Target Name="CopyNativeArtifacts" AfterTargets="Build" Condition="'$(DisableNativeCopy)'!='true'"> <Target Name="CopyNativeArtifacts" AfterTargets="Build" Condition="'$(DisableNativeCopy)'!='true'">
<Copy SourceFiles="@(NativeArtifacts)" DestinationFolder="$(TargetDir)" SkipUnchangedFiles="true" Condition="@(NativeArtifacts) != ''" /> <Copy SourceFiles="@(NativeArtifacts)" DestinationFolder="$(TargetDir)" SkipUnchangedFiles="true" Condition="@(NativeArtifacts) != ''" />
</Target> </Target>
</Project> </Project>
@@ -1,194 +1,194 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="BidirectionalDictionary" // file="BidirectionalDictionary"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
namespace StgSharp.Collections namespace StgSharp.Collections
{ {
public sealed class BidirectionalDictionary<TFirst, TSecond> : IBidirectionalDictionary<TFirst, TSecond> public sealed class BidirectionalDictionary<TFirst, TSecond> : IBidirectionalDictionary<TFirst, TSecond>
where TFirst: notnull where TFirst: notnull
where TSecond: notnull where TSecond: notnull
{ {
private readonly Dictionary<TFirst, TSecond> _forward; private readonly Dictionary<TFirst, TSecond> _forward;
private readonly Dictionary<TSecond, TFirst> _reverse; private readonly Dictionary<TSecond, TFirst> _reverse;
public BidirectionalDictionary() public BidirectionalDictionary()
{ {
_forward = []; _forward = [];
_reverse = []; _reverse = [];
} }
public TFirst this[TSecond key] public TFirst this[TSecond key]
{ {
get => _reverse.TryGetValue(key, out TFirst value) ? get => _reverse.TryGetValue(key, out TFirst value) ?
value : value :
throw new ArgumentException($"Cannot find key {key} in dictionary."); throw new ArgumentException($"Cannot find key {key} in dictionary.");
set set
{ {
if (_reverse.TryGetValue(key, out TFirst origin)) if (_reverse.TryGetValue(key, out TFirst origin))
{ {
_reverse[key] = value; _reverse[key] = value;
_ = _forward.Remove(origin); _ = _forward.Remove(origin);
} else } else
{ {
_reverse.Add(key, value); _reverse.Add(key, value);
} }
_forward.Add(value, key); _forward.Add(value, key);
} }
} }
public TSecond this[TFirst key] public TSecond this[TFirst key]
{ {
get => _forward.TryGetValue(key, out TSecond value) ? get => _forward.TryGetValue(key, out TSecond value) ?
value : value :
throw new ArgumentException($"Cannot find key {key} in dictionary."); throw new ArgumentException($"Cannot find key {key} in dictionary.");
set set
{ {
if (_forward.TryGetValue(key, out TSecond origin)) if (_forward.TryGetValue(key, out TSecond origin))
{ {
_forward[key] = value; _forward[key] = value;
_ = _reverse.Remove(origin); _ = _reverse.Remove(origin);
} else } else
{ {
_forward.Add(key, value); _forward.Add(key, value);
} }
_reverse.Add(value, key); _reverse.Add(value, key);
} }
} }
public bool IsReadOnly => false; public bool IsReadOnly => false;
public ICollection<TFirst> FirstIndex => _forward.Keys; public ICollection<TFirst> FirstIndex => _forward.Keys;
public ICollection<TSecond> SecondIndex => _reverse.Keys; public ICollection<TSecond> SecondIndex => _reverse.Keys;
public int Count => _forward.Count; public int Count => _forward.Count;
public IReadOnlyDictionary<TFirst, TSecond> Forward => new ReadOnlyDictionary<TFirst, TSecond>( public IReadOnlyDictionary<TFirst, TSecond> Forward => new ReadOnlyDictionary<TFirst, TSecond>(
_forward); _forward);
public IReadOnlyDictionary<TSecond, TFirst> Reverse => new ReadOnlyDictionary<TSecond, TFirst>( public IReadOnlyDictionary<TSecond, TFirst> Reverse => new ReadOnlyDictionary<TSecond, TFirst>(
_reverse); _reverse);
public void Add(KeyValuePair<TFirst, TSecond> item) public void Add(KeyValuePair<TFirst, TSecond> item)
{ {
if (_forward.ContainsKey(item.Key) || _reverse.ContainsKey(item.Value)) { if (_forward.ContainsKey(item.Key) || _reverse.ContainsKey(item.Value)) {
throw new ArgumentException("Duplicate key or value."); throw new ArgumentException("Duplicate key or value.");
} }
_forward.Add(item.Key, item.Value); _forward.Add(item.Key, item.Value);
_reverse.Add(item.Value, item.Key); _reverse.Add(item.Value, item.Key);
} }
public void Add(TFirst key, TSecond value) public void Add(TFirst key, TSecond value)
{ {
if (_forward.ContainsKey(key) || _reverse.ContainsKey(value)) { if (_forward.ContainsKey(key) || _reverse.ContainsKey(value)) {
throw new ArgumentException("Duplicate key or value."); throw new ArgumentException("Duplicate key or value.");
} }
_forward.Add(key, value); _forward.Add(key, value);
_reverse.Add(value, key); _reverse.Add(value, key);
} }
public void Clear() public void Clear()
{ {
_forward.Clear(); _forward.Clear();
_reverse.Clear(); _reverse.Clear();
} }
public bool Contains(KeyValuePair<TFirst, TSecond> item) public bool Contains(KeyValuePair<TFirst, TSecond> item)
{ {
return _forward.Contains(item) && _reverse.TryGetValue(item.Value, out TFirst var) && var!.Equals(item.Key); return _forward.Contains(item) && _reverse.TryGetValue(item.Value, out TFirst var) && var!.Equals(item.Key);
} }
public bool Contains(TSecond key) public bool Contains(TSecond key)
{ {
return _reverse.ContainsKey(key); return _reverse.ContainsKey(key);
} }
public bool Contains(TFirst key) public bool Contains(TFirst key)
{ {
return _forward.ContainsKey(key); return _forward.ContainsKey(key);
} }
public void CopyTo(KeyValuePair<TFirst, TSecond>[] array, int arrayIndex) public void CopyTo(KeyValuePair<TFirst, TSecond>[] array, int arrayIndex)
{ {
if (array == null) { if (array == null) {
throw new ArgumentNullException(nameof(array)); throw new ArgumentNullException(nameof(array));
} }
if (arrayIndex < 0 || arrayIndex > array.Length) { if (arrayIndex < 0 || arrayIndex > array.Length) {
throw new ArgumentOutOfRangeException(nameof(arrayIndex)); throw new ArgumentOutOfRangeException(nameof(arrayIndex));
} }
if (array.Length - arrayIndex < _forward.Count) { if (array.Length - arrayIndex < _forward.Count) {
throw new ArgumentException("The array is too small to copy the elements."); throw new ArgumentException("The array is too small to copy the elements.");
} }
foreach (KeyValuePair<TFirst, TSecond> pair in _forward) { foreach (KeyValuePair<TFirst, TSecond> pair in _forward) {
array[arrayIndex++] = pair; array[arrayIndex++] = pair;
} }
} }
public IEnumerator<KeyValuePair<TFirst, TSecond>> GetEnumerator() public IEnumerator<KeyValuePair<TFirst, TSecond>> GetEnumerator()
{ {
foreach (KeyValuePair<TFirst, TSecond> pair in _forward) { foreach (KeyValuePair<TFirst, TSecond> pair in _forward) {
yield return pair; yield return pair;
} }
} }
public bool Remove(TFirst key) public bool Remove(TFirst key)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public bool Remove(KeyValuePair<TFirst, TSecond> item) public bool Remove(KeyValuePair<TFirst, TSecond> item)
{ {
if (_forward.Remove(item.Key)) { if (_forward.Remove(item.Key)) {
return _reverse.Remove(item.Value); return _reverse.Remove(item.Value);
} }
return false; return false;
} }
public bool TryGetValue(TFirst key, out TSecond value) public bool TryGetValue(TFirst key, out TSecond value)
{ {
return _forward.TryGetValue(key, out value); return _forward.TryGetValue(key, out value);
} }
public bool TryGetValue(TSecond key, out TFirst value) public bool TryGetValue(TSecond key, out TFirst value)
{ {
return _reverse.TryGetValue(key, out value); return _reverse.TryGetValue(key, out value);
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
return GetEnumerator(); return GetEnumerator();
} }
} }
} }
+92 -92
View File
@@ -1,92 +1,92 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="CapacityFixedBag" // file="CapacityFixedBag"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
namespace StgSharp.Collections namespace StgSharp.Collections
{ {
public sealed class CapacityFixedBag<T> : IEnumerable<T> public sealed class CapacityFixedBag<T> : IEnumerable<T>
{ {
private readonly T[] _buffer; private readonly T[] _buffer;
public CapacityFixedBag(int capacity) public CapacityFixedBag(int capacity)
{ {
_buffer = new T[capacity]; _buffer = new T[capacity];
} }
public int Count { get; set; } public int Count { get; set; }
public int Capacity => _buffer.Length; public int Capacity => _buffer.Length;
public void Add(T item) public void Add(T item)
{ {
if (Count >= _buffer.Length) { if (Count >= _buffer.Length) {
throw new InvalidOperationException("Bag capacity exceeded."); throw new InvalidOperationException("Bag capacity exceeded.");
} }
_buffer[Count] = item; _buffer[Count] = item;
Count++; Count++;
} }
public IEnumerator<T> GetEnumerator() public IEnumerator<T> GetEnumerator()
{ {
for (int i = 0; i < Count; i++) { for (int i = 0; i < Count; i++) {
yield return _buffer[i]; yield return _buffer[i];
} }
} }
public void Remove(T item) public void Remove(T item)
{ {
int index = Array.IndexOf(_buffer, item, 0, Count); int index = Array.IndexOf(_buffer, item, 0, Count);
if (index >= 0) if (index >= 0)
{ {
_buffer[index] = _buffer[Count - 1]; _buffer[index] = _buffer[Count - 1];
_buffer[Count - 1] = default!; _buffer[Count - 1] = default!;
Count--; Count--;
} }
} }
public void RemoveAt(int index) public void RemoveAt(int index)
{ {
if (index >= 0) if (index >= 0)
{ {
_buffer[index] = _buffer[Count - 1]; _buffer[index] = _buffer[Count - 1];
_buffer[Count - 1] = default!; _buffer[Count - 1] = default!;
Count--; Count--;
} }
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
return GetEnumerator(); return GetEnumerator();
} }
} }
} }
+176 -176
View File
@@ -1,176 +1,176 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="CapacityFixedList" // file="CapacityFixedList"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace StgSharp.Collections namespace StgSharp.Collections
{ {
public static class CapacityFixedListBuilder public static class CapacityFixedListBuilder
{ {
internal static CapacityFixedList<T> Create<T>(ReadOnlySpan<T> values) internal static CapacityFixedList<T> Create<T>(ReadOnlySpan<T> values)
{ {
return new CapacityFixedList<T>(values); return new CapacityFixedList<T>(values);
} }
} }
[CollectionBuilder(typeof(CapacityFixedListBuilder), nameof(CapacityFixedListBuilder.Create))] [CollectionBuilder(typeof(CapacityFixedListBuilder), nameof(CapacityFixedListBuilder.Create))]
public class CapacityFixedList<T> : IList<T> public class CapacityFixedList<T> : IList<T>
{ {
private T[] _values; private T[] _values;
private readonly int _capacity; private readonly int _capacity;
private int _index; private int _index;
public CapacityFixedList(int size) public CapacityFixedList(int size)
{ {
_values = new T[size]; _values = new T[size];
_capacity = size; _capacity = size;
} }
public CapacityFixedList(ReadOnlySpan<T> values) public CapacityFixedList(ReadOnlySpan<T> values)
{ {
_values = values.ToArray(); _values = values.ToArray();
} }
public T this[int index] public T this[int index]
{ {
get => index < _index ? _values[index] : throw new ArgumentOutOfRangeException(nameof(index)); get => index < _index ? _values[index] : throw new ArgumentOutOfRangeException(nameof(index));
set => _values[index] = index < _index ? value : throw new ArgumentOutOfRangeException(nameof(index)); set => _values[index] = index < _index ? value : throw new ArgumentOutOfRangeException(nameof(index));
} }
public bool IsReadOnly => false; public bool IsReadOnly => false;
public int Count => _index + 1; public int Count => _index + 1;
public void Add(T item) public void Add(T item)
{ {
if (_index < _capacity - 1) if (_index < _capacity - 1)
{ {
_index++; _index++;
this[_index] = item; this[_index] = item;
} }
} }
public void Clear() public void Clear()
{ {
_values = new T[_capacity]; _values = new T[_capacity];
_index = 0; _index = 0;
} }
public bool Contains(T item) public bool Contains(T item)
{ {
for (int i = 0; i <= _index; i++) for (int i = 0; i <= _index; i++)
{ {
if (item.Equals(_values[i])) { if (item.Equals(_values[i])) {
return true; return true;
} }
} }
return false; return false;
} }
public void CopyTo(T[] array, int arrayIndex) public void CopyTo(T[] array, int arrayIndex)
{ {
_values.CopyTo(array, arrayIndex); _values.CopyTo(array, arrayIndex);
} }
public IEnumerator<T> GetEnumerator() public IEnumerator<T> GetEnumerator()
{ {
for (int i = 0; i <= _index; i++) { for (int i = 0; i <= _index; i++) {
yield return _values[i]; yield return _values[i];
} }
} }
public int IndexOf(T item) public int IndexOf(T item)
{ {
T v; T v;
for (int i = 0; i <= _index; i++) for (int i = 0; i <= _index; i++)
{ {
if ((v = _values[i]) != null && v.Equals(item)) { if ((v = _values[i]) != null && v.Equals(item)) {
return i; return i;
} }
} }
return -1; return -1;
} }
public bool Remove(T item) public bool Remove(T item)
{ {
T v; T v;
for (int i = 0; i <= _index; i++) for (int i = 0; i <= _index; i++)
{ {
if ((v = _values[i]) != null && v.Equals(item)) if ((v = _values[i]) != null && v.Equals(item))
{ {
_values[i] = _values[_index]; _values[i] = _values[_index];
_index--; _index--;
return true; return true;
} }
} }
return false; return false;
} }
public void RemoveAll(Predicate<T> predicate) public void RemoveAll(Predicate<T> predicate)
{ {
for (int i = _index; i > 0; i--) for (int i = _index; i > 0; i--)
{ {
if (predicate(_values[i])) if (predicate(_values[i]))
{ {
_values[i] = _values[_index]; _values[i] = _values[_index];
_index--; _index--;
} }
} }
} }
public void RemoveAt(int index) public void RemoveAt(int index)
{ {
if (index <= _index) if (index <= _index)
{ {
_values[index] = _values[_index]; _values[index] = _values[_index];
_index--; _index--;
} }
} }
public void Resize(int newSize) public void Resize(int newSize)
{ {
Array.Resize(ref _values, newSize); Array.Resize(ref _values, newSize);
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
yield return GetEnumerator(); yield return GetEnumerator();
} }
void IList<T>.Insert(int index, T item) void IList<T>.Insert(int index, T item)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
}//------------------------------------- End of Class ------------------------------------------ }//------------------------------------- End of Class ------------------------------------------
} }
+173 -173
View File
@@ -1,174 +1,174 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="CapacityFixedStack" // file="CapacityFixedStack"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
namespace StgSharp.Collections namespace StgSharp.Collections
{ {
[CollectionBuilder(typeof(CapacityFixedStackBuilder), nameof(CapacityFixedStackBuilder.Create))] [CollectionBuilder(typeof(CapacityFixedStackBuilder), nameof(CapacityFixedStackBuilder.Create))]
public class CapacityFixedStack<T> : IEnumerable<T> public class CapacityFixedStack<T> : IEnumerable<T>
{ {
private readonly T[] _values; private readonly T[] _values;
// _index points to the next free slot (top empty position). Therefore Count == _index. // _index points to the next free slot (top empty position). Therefore Count == _index.
private int _index; private int _index;
public CapacityFixedStack(int size) public CapacityFixedStack(int size)
{ {
ArgumentOutOfRangeException.ThrowIfNegative(size); ArgumentOutOfRangeException.ThrowIfNegative(size);
_values = new T[size]; _values = new T[size];
_index = 0; // empty stack _index = 0; // empty stack
} }
public T? First => Volatile.Read(ref _index) > 0 ? _values[0] : default; public T? First => Volatile.Read(ref _index) > 0 ? _values[0] : default;
public bool IsEmpty => Volatile.Read(ref _index) == 0; public bool IsEmpty => Volatile.Read(ref _index) == 0;
public int Count => Volatile.Read(ref _index); public int Count => Volatile.Read(ref _index);
public int Capacity => _values.Length; public int Capacity => _values.Length;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> AsSpan() public ReadOnlySpan<T> AsSpan()
{ {
return _values.AsSpan(0, _index); return _values.AsSpan(0, _index);
} }
public void Clear() public void Clear()
{ {
if (_index > 0) if (_index > 0)
{ {
Array.Clear(_values, 0, _index); Array.Clear(_values, 0, _index);
_index = 0; _index = 0;
} }
} }
public IEnumerator<T> GetEnumerator() public IEnumerator<T> GetEnumerator()
{ {
for (int i = _index - 1; i >= 0; i--) { for (int i = _index - 1; i >= 0; i--) {
yield return _values[i]; yield return _values[i];
} }
} }
public T Peek() public T Peek()
{ {
if (_index == 0) { if (_index == 0) {
throw new InvalidOperationException("Stack is empty."); throw new InvalidOperationException("Stack is empty.");
} }
return _values[_index - 1]; return _values[_index - 1];
} }
public T Pop() public T Pop()
{ {
if (_index == 0) { if (_index == 0) {
throw new InvalidOperationException("Stack is empty."); throw new InvalidOperationException("Stack is empty.");
} }
_index--; _index--;
T item = _values[_index]; T item = _values[_index];
_values[_index] = default!; // Clear reference for GC _values[_index] = default!; // Clear reference for GC
return item; return item;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] PopRange(int count) public T[] PopRange(int count)
{ {
if (count <= 0 || _index == 0) { if (count <= 0 || _index == 0) {
return Array.Empty<T>(); return Array.Empty<T>();
} }
int n = Math.Min(count, _index); int n = Math.Min(count, _index);
int start = _index - n; int start = _index - n;
T[] ret = new T[n]; T[] ret = new T[n];
Array.Copy(_values, start, ret, 0, n); Array.Copy(_values, start, ret, 0, n);
Array.Clear(_values, start, n); Array.Clear(_values, start, n);
_index -= n; _index -= n;
return ret; return ret;
} }
public void Push(T item) public void Push(T item)
{ {
if (_index >= _values.Length) { if (_index >= _values.Length) {
throw new InvalidOperationException("Stack is full."); throw new InvalidOperationException("Stack is full.");
} }
_values[_index] = item; _values[_index] = item;
_index++; _index++;
} }
public void PushRange(ReadOnlySpan<T> span) public void PushRange(ReadOnlySpan<T> span)
{ {
if (span.Length == 0) { if (span.Length == 0) {
return; return;
} }
if (_index + span.Length > _values.Length) { if (_index + span.Length > _values.Length) {
throw new InvalidOperationException("Not enough space in the stack to push the entire span."); throw new InvalidOperationException("Not enough space in the stack to push the entire span.");
} }
span.CopyTo(_values.AsSpan(_index, span.Length)); span.CopyTo(_values.AsSpan(_index, span.Length));
_index += span.Length; _index += span.Length;
} }
public bool TryPop(out T value) public bool TryPop(out T value)
{ {
if (_index == 0) if (_index == 0)
{ {
value = default!; value = default!;
return false; return false;
} }
_index--; _index--;
value = _values[_index]; value = _values[_index];
_values[_index] = default!; _values[_index] = default!;
return true; return true;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Span<T> GetValueLayout() internal Span<T> GetValueLayout()
{ {
return _values.AsSpan(); return _values.AsSpan();
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
return GetEnumerator(); return GetEnumerator();
} }
} }
public static class CapacityFixedStackBuilder public static class CapacityFixedStackBuilder
{ {
public static CapacityFixedStack<T> Create<T>(ReadOnlySpan<T> values) public static CapacityFixedStack<T> Create<T>(ReadOnlySpan<T> values)
{ {
CapacityFixedStack<T> stack = new(values.Length); CapacityFixedStack<T> stack = new(values.Length);
stack.PushRange(values); // Push in order stack.PushRange(values); // Push in order
return stack; return stack;
} }
} }
} }
@@ -1,287 +1,287 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="ConcurrentCapacityFixedStack" // file="ConcurrentCapacityFixedStack"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
namespace StgSharp.Collections namespace StgSharp.Collections
{ {
/// <summary> /// <summary>
/// Lock-free, fixed-capacity stack implemented on top of an array using atomic index /// 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 /// 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 /// reserve/release slots - Per-slot state flag to safely publish/consume values under races
/// </summary> /// </summary>
public class ConcurrentCapacityFixedStack<T> : IEnumerable<T> public class ConcurrentCapacityFixedStack<T> : IEnumerable<T>
{ {
private readonly int[] _states; // 0 = empty, 1 = full private readonly int[] _states; // 0 = empty, 1 = full
private readonly T[] _values; private readonly T[] _values;
private readonly int _capacity; private readonly int _capacity;
// Number of full slots (observable count) // Number of full slots (observable count)
private int _count; private int _count;
// Next push position (0.._capacity). Only modified via CAS. // Next push position (0.._capacity). Only modified via CAS.
private int _top; private int _top;
public ConcurrentCapacityFixedStack(int capacity) public ConcurrentCapacityFixedStack(int capacity)
{ {
if (capacity <= 0) { if (capacity <= 0) {
throw new ArgumentOutOfRangeException(nameof(capacity)); throw new ArgumentOutOfRangeException(nameof(capacity));
} }
_capacity = capacity; _capacity = capacity;
_values = new T[capacity]; _values = new T[capacity];
_states = new int[capacity]; _states = new int[capacity];
_top = 0; _top = 0;
_count = 0; _count = 0;
} }
public bool IsEmpty => Count == 0; public bool IsEmpty => Count == 0;
public int Capacity => _capacity; public int Capacity => _capacity;
public int Count => Volatile.Read(ref _count); public int Count => Volatile.Read(ref _count);
public void Clear() public void Clear()
{ {
// Pop all elements to return slots to empty state // Pop all elements to return slots to empty state
while (TryPop(out _)) { } while (TryPop(out _)) { }
} }
public IEnumerator<T> GetEnumerator() public IEnumerator<T> GetEnumerator()
{ {
T[] snap = ToArraySnapshot(); T[] snap = ToArraySnapshot();
for (int i = 0; i < snap.Length; i++) { for (int i = 0; i < snap.Length; i++) {
yield return snap[i]; yield return snap[i];
} }
} }
public T Peek() public T Peek()
{ {
if (TryPeek(out T? v)) { if (TryPeek(out T? v)) {
return v; return v;
} }
throw new InvalidOperationException("Stack is empty."); throw new InvalidOperationException("Stack is empty.");
} }
public T Pop() public T Pop()
{ {
if (TryPop(out T value)) { if (TryPop(out T value)) {
return value; return value;
} }
throw new InvalidOperationException("Stack is empty."); throw new InvalidOperationException("Stack is empty.");
} }
public T[] PopRange(int count) public T[] PopRange(int count)
{ {
if (count <= 0) { if (count <= 0) {
return []; return [];
} }
T[] result = new T[count]; T[] result = new T[count];
int i = 0; int i = 0;
for (; i < count; i++) for (; i < count; i++)
{ {
if (!TryPop(out result[i])) if (!TryPop(out result[i]))
{ {
break; break;
} }
} }
if (i == count) { if (i == count) {
return result; return result;
} }
if (i == 0) { if (i == 0) {
return []; return [];
} }
T[] trimmed = new T[i]; T[] trimmed = new T[i];
Array.Copy(result, trimmed, i); Array.Copy(result, trimmed, i);
return trimmed; return trimmed;
} }
public void Push(T item) public void Push(T item)
{ {
SpinWait sw = default; SpinWait sw = default;
while (true) while (true)
{ {
int oldTop = Volatile.Read(ref _top); int oldTop = Volatile.Read(ref _top);
if (oldTop >= _capacity) { if (oldTop >= _capacity) {
throw new InvalidOperationException("Stack is full."); throw new InvalidOperationException("Stack is full.");
} }
if (Interlocked.CompareExchange(ref _top, oldTop + 1, oldTop) != oldTop) if (Interlocked.CompareExchange(ref _top, oldTop + 1, oldTop) != oldTop)
{ {
sw.SpinOnce(); sw.SpinOnce();
continue; // failed to reserve slot continue; // failed to reserve slot
} }
// We exclusively own slot oldTop. Publish value then state. // We exclusively own slot oldTop. Publish value then state.
_values[oldTop] = item; _values[oldTop] = item;
Volatile.Write(ref _states[oldTop], 1); // publish Volatile.Write(ref _states[oldTop], 1); // publish
_ = Interlocked.Increment(ref _count); _ = Interlocked.Increment(ref _count);
return; return;
} }
} }
public void PushRange(ReadOnlySpan<T> span) public void PushRange(ReadOnlySpan<T> span)
{ {
for (int i = 0; i < span.Length; i++) { for (int i = 0; i < span.Length; i++) {
Push(span[i]); Push(span[i]);
} }
} }
/// <summary> /// <summary>
/// Best-effort snapshot for enumeration; not strongly consistent under concurrent /// Best-effort snapshot for enumeration; not strongly consistent under concurrent
/// mutations. /// mutations.
/// </summary> /// </summary>
public T[] ToArraySnapshot() public T[] ToArraySnapshot()
{ {
int expected = Math.Min(Volatile.Read(ref _count), _capacity); int expected = Math.Min(Volatile.Read(ref _count), _capacity);
if (expected == 0) { if (expected == 0) {
return Array.Empty<T>(); return Array.Empty<T>();
} }
T[] snap = new T[expected]; T[] snap = new T[expected];
int copied = 0; int copied = 0;
int idx = Volatile.Read(ref _top) - 1; int idx = Volatile.Read(ref _top) - 1;
while (idx >= 0 && copied < expected) while (idx >= 0 && copied < expected)
{ {
if (Volatile.Read(ref _states[idx]) == 1) { if (Volatile.Read(ref _states[idx]) == 1) {
snap[copied++] = _values[idx]; snap[copied++] = _values[idx];
} }
idx--; idx--;
} }
if (copied == snap.Length) { if (copied == snap.Length) {
return snap; return snap;
} }
T[] trimmed = new T[copied]; T[] trimmed = new T[copied];
Array.Copy(snap, trimmed, copied); Array.Copy(snap, trimmed, copied);
return trimmed; return trimmed;
} }
public bool TryPeek(out T value) public bool TryPeek(out T value)
{ {
value = default!; value = default!;
SpinWait sw = default; SpinWait sw = default;
while (true) while (true)
{ {
int top = Volatile.Read(ref _top); int top = Volatile.Read(ref _top);
if (top <= 0) { if (top <= 0) {
return false; return false;
} }
int idx = top - 1; int idx = top - 1;
// If not yet published or racing, retry/spin // If not yet published or racing, retry/spin
if (Volatile.Read(ref _states[idx]) == 0) if (Volatile.Read(ref _states[idx]) == 0)
{ {
if (Volatile.Read(ref _top) != top) if (Volatile.Read(ref _top) != top)
{ {
continue; // head moved, retry continue; // head moved, retry
} }
sw.SpinOnce(); sw.SpinOnce();
continue; continue;
} }
T v = _values[idx]; T v = _values[idx];
// Validate consistency // Validate consistency
if (Volatile.Read(ref _top) == top && Volatile.Read(ref _states[idx]) == 1) if (Volatile.Read(ref _top) == top && Volatile.Read(ref _states[idx]) == 1)
{ {
value = v; value = v;
return true; return true;
} }
} }
} }
public bool TryPop(out T value) public bool TryPop(out T value)
{ {
value = default!; value = default!;
SpinWait sw = default; SpinWait sw = default;
while (true) while (true)
{ {
int oldTop = Volatile.Read(ref _top); int oldTop = Volatile.Read(ref _top);
if (oldTop <= 0) if (oldTop <= 0)
{ {
return false; // empty return false; // empty
} }
int newTop = oldTop - 1; // index to consume int newTop = oldTop - 1; // index to consume
if (Interlocked.CompareExchange(ref _top, newTop, oldTop) != oldTop) if (Interlocked.CompareExchange(ref _top, newTop, oldTop) != oldTop)
{ {
sw.SpinOnce(); sw.SpinOnce();
continue; // failed to reserve element continue; // failed to reserve element
} }
// If producer hasn't published yet, roll back our reservation and retry. // If producer hasn't published yet, roll back our reservation and retry.
if (Volatile.Read(ref _states[newTop]) == 0) if (Volatile.Read(ref _states[newTop]) == 0)
{ {
// attempt to restore top; ok if it fails due to concurrent operations // attempt to restore top; ok if it fails due to concurrent operations
_ = Interlocked.CompareExchange(ref _top, newTop + 1, newTop); _ = Interlocked.CompareExchange(ref _top, newTop + 1, newTop);
sw.SpinOnce(); sw.SpinOnce();
continue; continue;
} }
value = _values[newTop]; value = _values[newTop];
_values[newTop] = default!; // avoid ref retention _values[newTop] = default!; // avoid ref retention
Volatile.Write(ref _states[newTop], 0); // mark empty Volatile.Write(ref _states[newTop], 0); // mark empty
_ = Interlocked.Decrement(ref _count); _ = Interlocked.Decrement(ref _count);
return true; return true;
} }
} }
public int TryPopRange(Span<T> destination) public int TryPopRange(Span<T> destination)
{ {
if (destination.IsEmpty) { if (destination.IsEmpty) {
return 0; return 0;
} }
int i = 0; int i = 0;
for (; i < destination.Length; i++) for (; i < destination.Length; i++)
{ {
if (!TryPop(out destination[i])) if (!TryPop(out destination[i]))
{ {
break; break;
} }
} }
return i; return i;
} }
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
} }
} }
@@ -1,84 +1,84 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="IBidirectionalDictionary" // file="IBidirectionalDictionary"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System.Collections.Generic; using System.Collections.Generic;
namespace StgSharp.Collections namespace StgSharp.Collections
{ {
public interface IBidirectionalDictionary<TFirst, TSecond> : IDictionary<TFirst, TSecond> public interface IBidirectionalDictionary<TFirst, TSecond> : IDictionary<TFirst, TSecond>
{ {
TFirst this[TSecond key] { get; set; } TFirst this[TSecond key] { get; set; }
ICollection<TFirst> FirstIndex { get; } ICollection<TFirst> FirstIndex { get; }
ICollection<TSecond> SecondIndex { get; } ICollection<TSecond> SecondIndex { get; }
IReadOnlyDictionary<TFirst, TSecond> Forward { get; } IReadOnlyDictionary<TFirst, TSecond> Forward { get; }
IReadOnlyDictionary<TSecond, TFirst> Reverse { get; } IReadOnlyDictionary<TSecond, TFirst> Reverse { get; }
/// <summary> /// <summary>
/// Determines whether the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains the /// Determines whether the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains the
/// specified key. /// specified key.
/// </summary> /// </summary>
/// <param _label="key"> /// <param _label="key">
/// The value to locate in the <see cref="IBidirectionalDictionary{TFirst, TSecond}" />. /// The value to locate in the <see cref="IBidirectionalDictionary{TFirst, TSecond}" />.
/// </param> /// </param>
/// <returns> /// <returns>
/// true if the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains an element with /// true if the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains an element with
/// the specified key; otherwise, false. /// the specified key; otherwise, false.
/// </returns> /// </returns>
bool Contains(TSecond key); bool Contains(TSecond key);
/// <summary> /// <summary>
/// Determines whether the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains the /// Determines whether the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains the
/// specified key. /// specified key.
/// </summary> /// </summary>
/// <param _label="key"> /// <param _label="key">
/// The value to locate in the <see cref="IBidirectionalDictionary{TFirst, TSecond}" />. /// The value to locate in the <see cref="IBidirectionalDictionary{TFirst, TSecond}" />.
/// </param> /// </param>
/// <returns> /// <returns>
/// true if the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains an element with /// true if the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains an element with
/// the specified key; otherwise, false. /// the specified key; otherwise, false.
/// </returns> /// </returns>
bool Contains(TFirst key); bool Contains(TFirst key);
bool TryGetValue(TSecond key, out TFirst value); bool TryGetValue(TSecond key, out TFirst value);
#pragma warning disable CA1033 #pragma warning disable CA1033
ICollection<TFirst> IDictionary<TFirst, TSecond>.Keys => FirstIndex; ICollection<TFirst> IDictionary<TFirst, TSecond>.Keys => FirstIndex;
ICollection<TSecond> IDictionary<TFirst, TSecond>.Values => SecondIndex; ICollection<TSecond> IDictionary<TFirst, TSecond>.Values => SecondIndex;
bool IDictionary<TFirst, TSecond>.ContainsKey(TFirst key) bool IDictionary<TFirst, TSecond>.ContainsKey(TFirst key)
{ {
return Contains(key); return Contains(key);
} }
#pragma warning restore CA1033 #pragma warning restore CA1033
} }
} }
+52 -52
View File
@@ -1,53 +1,53 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="IHashMultiplexer" // file="IHashMultiplexer"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System.Collections.Generic; using System.Collections.Generic;
namespace StgSharp.Collections namespace StgSharp.Collections
{ {
public interface IHashMultiplexer<T> : IEqualityComparer<T> public interface IHashMultiplexer<T> : IEqualityComparer<T>
{ {
int RemainedMultiplexCount { get; set; } int RemainedMultiplexCount { get; set; }
/// <summary> /// <summary>
/// Cache hash result for obj with counts. /// Cache hash result for obj with counts.
/// </summary> /// </summary>
/// <param name="obj"> /// <param name="obj">
/// ///
/// </param> /// </param>
/// <param name="count"> /// <param name="count">
/// ///
/// </param> /// </param>
void CacheHash(T obj, int count); void CacheHash(T obj, int count);
void ExtendCache(int count); void ExtendCache(int count);
int ForceComputeHash(T count); int ForceComputeHash(T count);
} }
} }
+131 -131
View File
@@ -1,131 +1,131 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="ListSegment" // file="ListSegment"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml.Serialization; using System.Xml.Serialization;
namespace StgSharp.Collections namespace StgSharp.Collections
{ {
/// <summary> /// <summary>
/// Part of a collection. This contains a direct reference to _origin collection, so all /// Part of a collection. This contains a direct reference to _origin collection, so all
/// change to _origin collection will be reflected in this object. /// change to _origin collection will be reflected in this object.
/// </summary> /// </summary>
/// <typeparam name="T"> /// <typeparam name="T">
/// ///
/// </typeparam> /// </typeparam>
public struct ListSegment<T> : ICollection<T> public struct ListSegment<T> : ICollection<T>
{ {
private int _begin; private int _begin;
private int _length; private int _length;
private List<T> _collection; private List<T> _collection;
public ListSegment(List<T> collection, int begin, int length) public ListSegment(List<T> collection, int begin, int length)
{ {
ArgumentNullException.ThrowIfNull(collection); ArgumentNullException.ThrowIfNull(collection);
ArgumentOutOfRangeException.ThrowIfLessThan(begin, 0); ArgumentOutOfRangeException.ThrowIfLessThan(begin, 0);
ArgumentOutOfRangeException.ThrowIfLessThan(length, 0); ArgumentOutOfRangeException.ThrowIfLessThan(length, 0);
ArgumentOutOfRangeException.ThrowIfLessThan(begin + length, collection.Count); ArgumentOutOfRangeException.ThrowIfLessThan(begin + length, collection.Count);
_collection = collection; _collection = collection;
_begin = begin; _begin = begin;
_length = length; _length = length;
} }
public T this[int indexValue] public T this[int indexValue]
{ {
get get
{ {
ArgumentOutOfRangeException.ThrowIfLessThan(indexValue, 0); ArgumentOutOfRangeException.ThrowIfLessThan(indexValue, 0);
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(indexValue, _length); ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(indexValue, _length);
return _collection[indexValue + _begin]; return _collection[indexValue + _begin];
} }
} }
public T this[Index i] public T this[Index i]
{ {
get get
{ {
int offset = i.IsFromEnd ? _length - i.Value : i.Value; int offset = i.IsFromEnd ? _length - i.Value : i.Value;
ArgumentOutOfRangeException.ThrowIfLessThan(offset, 0); ArgumentOutOfRangeException.ThrowIfLessThan(offset, 0);
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(offset, _length); ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(offset, _length);
return _collection[offset + _begin]; return _collection[offset + _begin];
} }
} }
public bool IsReadOnly => true; public bool IsReadOnly => true;
public int Count => _length; public int Count => _length;
public bool Contains(T item) public bool Contains(T item)
{ {
int index = _collection.IndexOf(item); int index = _collection.IndexOf(item);
return index >= _begin && index < _length + index; return index >= _begin && index < _length + index;
} }
public void CopyTo(T[] array, int arrayIndex) public void CopyTo(T[] array, int arrayIndex)
{ {
ArgumentNullException.ThrowIfNull(array); ArgumentNullException.ThrowIfNull(array);
Span<T> span = CollectionsMarshal.AsSpan<T>(_collection).Slice(_begin, _length); Span<T> span = CollectionsMarshal.AsSpan<T>(_collection).Slice(_begin, _length);
Span<T> target = new Span<T>(array, arrayIndex, _length); Span<T> target = new Span<T>(array, arrayIndex, _length);
span.CopyTo(target); span.CopyTo(target);
} }
public IEnumerator<T> GetEnumerator() public IEnumerator<T> GetEnumerator()
{ {
for (int i = _begin; i < _length; i++) { for (int i = _begin; i < _length; i++) {
yield return this[i]; yield return this[i];
} }
} }
void ICollection<T>.Add(T item) void ICollection<T>.Add(T item)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
void ICollection<T>.Clear() void ICollection<T>.Clear()
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
bool ICollection<T>.Remove(T item) bool ICollection<T>.Remove(T item)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
return GetEnumerator(); return GetEnumerator();
} }
} }
} }
+200 -200
View File
@@ -1,200 +1,200 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="NotEnumerableList.cs" // file="NotEnumerableList.cs"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person // Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation // obtaining a copy of this software and associated documentation
// files (the “Software”), to deal in the Software without restriction, // files (the “Software”), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, // including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, // publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so, // and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions: // subject to the following conditions:
// //
// The above copyright notice and // The above copyright notice and
// this permission notice shall be included in all copies // this permission notice shall be included in all copies
// or substantial portions of the Software. // or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED “AS IS”, // THE SOFTWARE IS PROVIDED “AS IS”,
// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // 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, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH // ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace StgSharp.Collections namespace StgSharp.Collections
{ {
public static class LinearBagBuilder public static class LinearBagBuilder
{ {
/// <summary> /// <summary>
/// Build a <see cref="LinearBag{T}" /> from a read-only span. Capacity will exactly fit /// Build a <see cref="LinearBag{T}" /> from a read-only span. Capacity will exactly fit
/// the input. /// the input.
/// </summary> /// </summary>
public static LinearBag<T> FromSpan<T>(ReadOnlySpan<T> values) public static LinearBag<T> FromSpan<T>(ReadOnlySpan<T> values)
{ {
if (values.IsEmpty) { if (values.IsEmpty) {
return new LinearBag<T>(4); return new LinearBag<T>(4);
} }
LinearBag<T> bag = new(values.Length); LinearBag<T> bag = new(values.Length);
for (int i = 0; i < values.Length; i++) { for (int i = 0; i < values.Length; i++) {
bag.Add(values[i]); bag.Add(values[i]);
} }
return bag; return bag;
} }
} }
/// <summary> /// <summary>
/// A very lightweight contiguous storage structure: unordered, not enumerable by design. /// 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- /// 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. /// performance scenarios where element ordering does not matter. NOT thread-safe.
/// </summary> /// </summary>
/// <typeparam name="T"> /// <typeparam name="T">
/// Element type. /// Element type.
/// </typeparam> /// </typeparam>
[CollectionBuilder(typeof(LinearBagBuilder), nameof(LinearBagBuilder.FromSpan))] [CollectionBuilder(typeof(LinearBagBuilder), nameof(LinearBagBuilder.FromSpan))]
public class LinearBag<T>(int capacity = 4) public class LinearBag<T>(int capacity = 4)
{ {
// Invariant: valid elements occupy indexes [0, Count-1]. Count is the number of live elements. // Invariant: valid elements occupy indexes [0, Count-1]. Count is the number of live elements.
private T[] _items = new T[Math.Max(0, capacity)]; private T[] _items = new T[Math.Max(0, capacity)];
/// <summary> /// <summary>
/// Access element by index (0..Count-1). Throws if out of range. /// Access element by index (0..Count-1). Throws if out of range.
/// </summary> /// </summary>
public T this[int index] public T this[int index]
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (uint)index >= (uint)Count ? throw new ArgumentOutOfRangeException(nameof(index)) : _items[index]; get => (uint)index >= (uint)Count ? throw new ArgumentOutOfRangeException(nameof(index)) : _items[index];
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
set set
{ {
if ((uint)index >= (uint)Count) { if ((uint)index >= (uint)Count) {
throw new ArgumentOutOfRangeException(nameof(index)); throw new ArgumentOutOfRangeException(nameof(index));
} }
_items[index] = value; _items[index] = value;
} }
} }
/// <summary> /// <summary>
/// Total allocated slots. /// Total allocated slots.
/// </summary> /// </summary>
public int Capacity => _items.Length; public int Capacity => _items.Length;
/// <summary> /// <summary>
/// Number of currently stored elements (valid index range 0..Count-1). /// Number of currently stored elements (valid index range 0..Count-1).
/// </summary> /// </summary>
public int Count { get; private set; } public int Count { get; private set; }
/// <summary> /// <summary>
/// Adds an item (unordered). Expands internal array if needed. /// Adds an item (unordered). Expands internal array if needed.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(T item) public void Add(T item)
{ {
if (Count == _items.Length) { if (Count == _items.Length) {
EnsureCapacity(Count + 1); EnsureCapacity(Count + 1);
} }
_items[Count] = item; _items[Count] = item;
Count++; Count++;
} }
/// <summary> /// <summary>
/// Returns a span over the live portion of the bag (0..Count-1). /// Returns a span over the live portion of the bag (0..Count-1).
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan() public Span<T> AsSpan()
{ {
return new Span<T>(_items, 0, Count); return new Span<T>(_items, 0, Count);
} }
/// <summary> /// <summary>
/// Clears all elements and zeroes out references to avoid retention. Count becomes 0. /// Clears all elements and zeroes out references to avoid retention. Count becomes 0.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() public void Clear()
{ {
Array.Clear(_items, 0, Count); Array.Clear(_items, 0, Count);
Count = 0; Count = 0;
} }
/// <summary> /// <summary>
/// Fast clear: only resets Count (leaves underlying array unchanged). References are NOT /// Fast clear: only resets Count (leaves underlying array unchanged). References are NOT
/// released. Use only when you are certain old references can remain. /// released. Use only when you are certain old references can remain.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FastClear() public void FastClear()
{ {
Count = 0; Count = 0;
} }
/// <summary> /// <summary>
/// Removes the first occurrence of value (unordered). Does nothing if not found. /// Removes the first occurrence of value (unordered). Does nothing if not found.
/// </summary> /// </summary>
public void Remove(T value) public void Remove(T value)
{ {
EqualityComparer<T> comparer = EqualityComparer<T>.Default; EqualityComparer<T> comparer = EqualityComparer<T>.Default;
for (int i = 0; i < Count; i++) for (int i = 0; i < Count; i++)
{ {
if (comparer.Equals(_items[i], value)) if (comparer.Equals(_items[i], value))
{ {
RemoveAt(i); RemoveAt(i);
return; return;
} }
} }
} }
/// <summary> /// <summary>
/// Removes element at index by swapping the last element into its slot (unless already /// Removes element at index by swapping the last element into its slot (unless already
/// last). Preserves O(1) removal at cost of ordering. /// last). Preserves O(1) removal at cost of ordering.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAt(int index) public void RemoveAt(int index)
{ {
if ((uint)index >= (uint)Count) { if ((uint)index >= (uint)Count) {
throw new ArgumentOutOfRangeException(nameof(index)); throw new ArgumentOutOfRangeException(nameof(index));
} }
int last = Count - 1; int last = Count - 1;
if (index != last) { if (index != last) {
_items[index] = _items[last]; _items[index] = _items[last];
} }
_items[last] = default!; // release reference / reset value _items[last] = default!; // release reference / reset value
Count = last; Count = last;
} }
/// <summary> /// <summary>
/// Ensures capacity is at least <paramref name="min" />. Doubles size when growing. /// Ensures capacity is at least <paramref name="min" />. Doubles size when growing.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnsureCapacity(int min) private void EnsureCapacity(int min)
{ {
if (_items.Length >= min) { if (_items.Length >= min) {
return; return;
} }
int newCapacity = _items.Length == 0 ? 4 : _items.Length * 2; int newCapacity = _items.Length == 0 ? 4 : _items.Length * 2;
if (newCapacity < min) { if (newCapacity < min) {
newCapacity = min; newCapacity = min;
} }
Array.Resize(ref _items, newCapacity); Array.Resize(ref _items, newCapacity);
} }
} }
} }
@@ -1,181 +1,181 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="RandomAccessibleStack" // file="RandomAccessibleStack"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
namespace StgSharp.Collections namespace StgSharp.Collections
{ {
public class RandomAccessibleStack<T> : IEnumerable<T> public class RandomAccessibleStack<T> : IEnumerable<T>
{ {
private const int DefaultCapacity = 4; private const int DefaultCapacity = 4;
private T[] _array; private T[] _array;
private int _size; private int _size;
private int _version; private int _version;
/// <summary> /// <summary>
/// Initializes a new instance of the RandomAccessStack class that is empty and has the /// Initializes a new instance of the RandomAccessStack class that is empty and has the
/// default initial capacity. /// default initial capacity.
/// </summary> /// </summary>
public RandomAccessibleStack() public RandomAccessibleStack()
{ {
_array = new T[DefaultCapacity]; _array = new T[DefaultCapacity];
} }
/// <summary> /// <summary>
/// Initializes a new instance of the RandomAccessStack class that is empty and has the /// Initializes a new instance of the RandomAccessStack class that is empty and has the
/// specified initial capacity. /// specified initial capacity.
/// </summary> /// </summary>
public RandomAccessibleStack(int capacity) public RandomAccessibleStack(int capacity)
{ {
if (capacity < 0) { if (capacity < 0) {
throw new ArgumentOutOfRangeException( throw new ArgumentOutOfRangeException(
nameof(capacity), "Capacity must be non-negative."); nameof(capacity), "Capacity must be non-negative.");
} }
_array = new T[capacity]; _array = new T[capacity];
} }
/// <summary> /// <summary>
/// Gets or sets the element at the specified index in the stack (0 = bottom, Count-1 = /// Gets or sets the element at the specified index in the stack (0 = bottom, Count-1 =
/// top). /// top).
/// </summary> /// </summary>
public T this[int index] public T this[int index]
{ {
get get
{ {
if (index < 0 || index >= _size) { if (index < 0 || index >= _size) {
throw new ArgumentOutOfRangeException(nameof(index)); throw new ArgumentOutOfRangeException(nameof(index));
} }
return _array[index]; return _array[index];
} }
/* /*
set set
{ {
if( index < 0 || index >= PrimSize ) { if( index < 0 || index >= PrimSize ) {
throw new ArgumentOutOfRangeException( nameof( index ) ); throw new ArgumentOutOfRangeException( nameof( index ) );
} }
_array[ index ] = value; _array[ index ] = value;
_version++; _version++;
} }
/**/ /**/
} }
/// <summary> /// <summary>
/// Gets the number of elements contained in the stack. /// Gets the number of elements contained in the stack.
/// </summary> /// </summary>
public int Count => _size; public int Count => _size;
/// <summary> /// <summary>
/// Removes all objects from the stack. /// Removes all objects from the stack.
/// </summary> /// </summary>
public void Clear() public void Clear()
{ {
Array.Clear(_array, 0, _size); Array.Clear(_array, 0, _size);
_size = 0; _size = 0;
_version++; _version++;
} }
/// <summary> /// <summary>
/// Returns an enumerator that iterates through the stack from top to bottom. Throws if /// Returns an enumerator that iterates through the stack from top to bottom. Throws if
/// the stack is modified during iteration. /// the stack is modified during iteration.
/// </summary> /// </summary>
public IEnumerator<T> GetEnumerator() public IEnumerator<T> GetEnumerator()
{ {
int expectedVersion = _version; int expectedVersion = _version;
for (int i = _size - 1; i >= 0; i--) for (int i = _size - 1; i >= 0; i--)
{ {
if (expectedVersion != _version) { if (expectedVersion != _version) {
throw new InvalidOperationException( throw new InvalidOperationException(
"Collection was modified during enumeration."); "Collection was modified during enumeration.");
} }
yield return _array[i]; yield return _array[i];
} }
} }
/// <summary> /// <summary>
/// Returns the object at the top of the stack without removing it. /// Returns the object at the top of the stack without removing it.
/// </summary> /// </summary>
public T Peek() public T Peek()
{ {
if (_size == 0) { if (_size == 0) {
throw new InvalidOperationException("Stack is empty."); throw new InvalidOperationException("Stack is empty.");
} }
return _array[_size - 1]; return _array[_size - 1];
} }
/// <summary> /// <summary>
/// Removes and returns the object at the top of the stack. /// Removes and returns the object at the top of the stack.
/// </summary> /// </summary>
public T Pop() public T Pop()
{ {
if (_size == 0) { if (_size == 0) {
throw new InvalidOperationException("Stack is empty."); throw new InvalidOperationException("Stack is empty.");
} }
T item = _array[--_size]; T item = _array[--_size];
_array[_size] = default!; _array[_size] = default!;
_version++; _version++;
return item; return item;
} }
/// <summary> /// <summary>
/// Adds an item to the top of the stack. /// Adds an item to the top of the stack.
/// </summary> /// </summary>
public void Push(T item) public void Push(T item)
{ {
if (_size == _array.Length) { if (_size == _array.Length) {
EnsureCapacity(_size + 1); EnsureCapacity(_size + 1);
} }
_array[_size++] = item; _array[_size++] = item;
_version++; _version++;
} }
private void EnsureCapacity(int min) private void EnsureCapacity(int min)
{ {
int newCapacity = _array.Length == 0 ? DefaultCapacity : _array.Length * 2; int newCapacity = _array.Length == 0 ? DefaultCapacity : _array.Length * 2;
if (newCapacity < min) { if (newCapacity < min) {
newCapacity = min; newCapacity = min;
} }
Array.Resize(ref _array, newCapacity); Array.Resize(ref _array, newCapacity);
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
return GetEnumerator(); return GetEnumerator();
} }
} }
} }
+189 -189
View File
@@ -1,91 +1,91 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="SwissTable" // file="SwissTable"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
namespace StgSharp.Collections namespace StgSharp.Collections
{ {
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public sealed unsafe partial class SwissTable public sealed unsafe partial class SwissTable
{ {
private const int BucketCount = 1 << EntryMaskWidth; private const int BucketCount = 1 << EntryMaskWidth;
private const int BucketSize = 16; private const int BucketSize = 16;
private const uint CrcMagic = 0xffff9961; // Magic number for CRC32C, used in hash computation private const uint CrcMagic = 0xffff9961; // Magic number for CRC32C, used in hash computation
private const int EntryMaskWidth = 16; // 65536 entries total, 16 per bucket private const int EntryMaskWidth = 16; // 65536 entries total, 16 per bucket
public const int MaxCapacity = BucketCount * BucketSize; // 256 entries per bucket, 1024 buckets public const int MaxCapacity = BucketCount * BucketSize; // 256 entries per bucket, 1024 buckets
private readonly Entry* _entries; private readonly Entry* _entries;
private SwissTable( private SwissTable(
Entry* buffer Entry* buffer
) )
{ {
_entries = buffer; _entries = buffer;
} }
public static SwissTable Create() public static SwissTable Create()
{ {
Entry* buffer = (Entry*)NativeMemory.AlignedAlloc((nuint)Unsafe.SizeOf<Entry>(), 16); Entry* buffer = (Entry*)NativeMemory.AlignedAlloc((nuint)Unsafe.SizeOf<Entry>(), 16);
for (int i = 0; i < BucketCount; i++) for (int i = 0; i < BucketCount; i++)
{ {
// Set all control bytes to 0xFF (empty) // Set all control bytes to 0xFF (empty)
Unsafe.InitBlock(&(*buffer)[i].Control, 0xFF, BucketSize); Unsafe.InitBlock(&(*buffer)[i].Control, 0xFF, BucketSize);
} }
return new SwissTable(buffer); return new SwissTable(buffer);
} }
public bool TryAddOrSet( public bool TryAddOrSet(
nint key, nint key,
nint value, nint value,
out bool isNewKey out bool isNewKey
) )
{ {
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key); uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
ushort entry = (ushort)(hash & (BucketCount - 1)); ushort entry = (ushort)(hash & (BucketCount - 1));
Bucket* bucket = &(*_entries)[entry]; Bucket* bucket = &(*_entries)[entry];
return IBucketScanner.Current.TrySet(key, hash, bucket, value, out isNewKey); return IBucketScanner.Current.TrySet(key, hash, bucket, value, out isNewKey);
} }
public bool TryGet( public bool TryGet(
nint key, nint key,
out nint value out nint value
) )
{ {
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key); uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
ushort entry = (ushort)(hash & (BucketCount - 1)); ushort entry = (ushort)(hash & (BucketCount - 1));
Bucket* bucket = &(*_entries)[entry]; Bucket* bucket = &(*_entries)[entry];
return IBucketScanner.Current.TryFind(key, hash, bucket, out value); return IBucketScanner.Current.TryFind(key, hash, bucket, out value);
/* /*
* hash = CRC32 of key * hash = CRC32 of key
@@ -93,108 +93,108 @@ namespace StgSharp.Collections
* ctrl = 8-15 of hash * ctrl = 8-15 of hash
* index = rest of hash * index = rest of hash
* find value of hash * find value of hash
*/ */
} }
public bool TryRemove( public bool TryRemove(
nint key, nint key,
out nint value out nint value
) )
{ {
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key); uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
ushort entry = (ushort)(hash & (BucketCount - 1)); ushort entry = (ushort)(hash & (BucketCount - 1));
Bucket* bucket = &(*_entries)[entry]; Bucket* bucket = &(*_entries)[entry];
return IBucketScanner.Current.TryRemove(key, hash, bucket, out value); return IBucketScanner.Current.TryRemove(key, hash, bucket, out value);
} }
~SwissTable() ~SwissTable()
{ {
NativeMemory.AlignedFree(_entries); NativeMemory.AlignedFree(_entries);
} }
[InlineArray(BucketCount)] [InlineArray(BucketCount)]
private struct Entry private struct Entry
{ {
public Bucket Bucket; public Bucket Bucket;
} }
private unsafe struct Bucket private unsafe struct Bucket
{ {
public BucketControl Control; public BucketControl Control;
public BucketKeyStorage Keys; public BucketKeyStorage Keys;
public BucketValueStorage Values; public BucketValueStorage Values;
} }
private interface IBucketScanner private interface IBucketScanner
{ {
static IBucketScanner Current { get; } = Create(); static IBucketScanner Current { get; } = Create();
bool TryFind( bool TryFind(
nint key, nint key,
uint Hash, uint Hash,
Bucket* bucket, Bucket* bucket,
out nint value out nint value
); );
unsafe bool TryRemove( unsafe bool TryRemove(
nint key, nint key,
uint hash, uint hash,
Bucket* bucket, Bucket* bucket,
out nint value out nint value
); );
bool TrySet( bool TrySet(
nint key, nint key,
uint hash, uint hash,
Bucket* bucket, Bucket* bucket,
nint value, nint value,
out bool isNewKey out bool isNewKey
); );
private static IBucketScanner Create() private static IBucketScanner Create()
{ {
if (Sse42.IsSupported) if (Sse42.IsSupported)
{ {
return new BucketScannerX86(); return new BucketScannerX86();
} else if (ArmBase.IsSupported) } else if (ArmBase.IsSupported)
{ {
throw new NotSupportedException("ARM architecture is currently not supported for bucket scanning. Please use an x86 platform."); throw new NotSupportedException("ARM architecture is currently not supported for bucket scanning. Please use an x86 platform.");
} else } else
{ {
throw new NotSupportedException("No suitable bucket scanner implementation found for the current platform."); throw new NotSupportedException("No suitable bucket scanner implementation found for the current platform.");
} }
} }
} }
[InlineArray(BucketSize)] [InlineArray(BucketSize)]
private struct BucketControl private struct BucketControl
{ {
public byte Control; public byte Control;
} }
[InlineArray(BucketSize)] [InlineArray(BucketSize)]
private struct BucketKeyStorage private struct BucketKeyStorage
{ {
public nint value; public nint value;
} }
[InlineArray(BucketSize)] [InlineArray(BucketSize)]
private struct BucketValueStorage private struct BucketValueStorage
{ {
public nint value; public nint value;
} }
} }
} }
+190 -190
View File
@@ -1,190 +1,190 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="Dialogue" // file="Dialogue"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Pipes; using System.IO.Pipes;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
namespace StgSharp namespace StgSharp
{ {
public static class Dialogue public static class Dialogue
{ {
private static Process? _dialogueProcess; private static Process? _dialogueProcess;
private static AnonymousPipeServerStream _dialogueClientPipe,_dialogueServerPipe; private static AnonymousPipeServerStream _dialogueClientPipe,_dialogueServerPipe;
private static int _readAndWriteThreadId; private static int _readAndWriteThreadId;
private static ProcessStartInfo _dialogueProcessInfo; private static ProcessStartInfo _dialogueProcessInfo;
private static StreamReader _dr; private static StreamReader _dr;
private static StreamWriter _dw; private static StreamWriter _dw;
public static bool NeedShowWhenStartup { get; set; } public static bool NeedShowWhenStartup { get; set; }
public static void PostError( public static void PostError(
Exception ex Exception ex
) )
{ {
CreateDialogueProcessIfNotExist(); CreateDialogueProcessIfNotExist();
_dw.WriteLine($"posterror {ex.Message}"); _dw.WriteLine($"posterror {ex.Message}");
_dialogueProcess!.Exited -= DialogueProcessExitCallback; _dialogueProcess!.Exited -= DialogueProcessExitCallback;
} }
public static void SetReadAndWriteThread( public static void SetReadAndWriteThread(
Thread thread Thread thread
) )
{ {
_readAndWriteThreadId = thread.ManagedThreadId; _readAndWriteThreadId = thread.ManagedThreadId;
} }
internal static void CreateDialogueProcessIfNotExist() internal static void CreateDialogueProcessIfNotExist()
{ {
if (_dialogueProcess != null) { if (_dialogueProcess != null) {
return; return;
} }
if (!File.Exists(_dialogueProcessInfo.FileName)) { if (!File.Exists(_dialogueProcessInfo.FileName)) {
return; return;
} }
_dialogueProcess = Process.Start(_dialogueProcessInfo); _dialogueProcess = Process.Start(_dialogueProcessInfo);
_dr = new StreamReader(_dialogueClientPipe); _dr = new StreamReader(_dialogueClientPipe);
_dw = new StreamWriter(_dialogueServerPipe); _dw = new StreamWriter(_dialogueServerPipe);
_dialogueProcess!.Exited += DialogueProcessExitCallback; _dialogueProcess!.Exited += DialogueProcessExitCallback;
_dialogueServerPipe.DisposeLocalCopyOfClientHandle(); _dialogueServerPipe.DisposeLocalCopyOfClientHandle();
_dialogueClientPipe.DisposeLocalCopyOfClientHandle(); _dialogueClientPipe.DisposeLocalCopyOfClientHandle();
} }
internal static void LoadDialogue() internal static void LoadDialogue()
{ {
_dialogueServerPipe = new AnonymousPipeServerStream( _dialogueServerPipe = new AnonymousPipeServerStream(
PipeDirection.Out, HandleInheritability.Inheritable); PipeDirection.Out, HandleInheritability.Inheritable);
_dialogueClientPipe = new AnonymousPipeServerStream( _dialogueClientPipe = new AnonymousPipeServerStream(
PipeDirection.In, HandleInheritability.Inheritable); PipeDirection.In, HandleInheritability.Inheritable);
string route = Assembly.GetAssembly(typeof(World))?.Location ?? string.Empty; string route = Assembly.GetAssembly(typeof(World))?.Location ?? string.Empty;
string path = Path.GetDirectoryName(route) ?? string.Empty; string path = Path.GetDirectoryName(route) ?? string.Empty;
string exeName; string exeName;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
exeName = ".exe"; exeName = ".exe";
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{ {
exeName = string.Empty; exeName = string.Empty;
} else } else
{ {
throw new PlatformNotSupportedException("Unknown OS platform"); throw new PlatformNotSupportedException("Unknown OS platform");
} }
string consoleRoute = Path.Combine( string consoleRoute = Path.Combine(
path, @"StgSharp.TerminalDialogue", $@"StgSharp.TerminalDialogue{exeName}"); path, @"StgSharp.TerminalDialogue", $@"StgSharp.TerminalDialogue{exeName}");
_dialogueProcessInfo = new ProcessStartInfo _dialogueProcessInfo = new ProcessStartInfo
{ {
FileName = consoleRoute, FileName = consoleRoute,
Arguments = Arguments =
$"{_dialogueServerPipe.GetClientHandleAsString()} {_dialogueClientPipe.GetClientHandleAsString()}", $"{_dialogueServerPipe.GetClientHandleAsString()} {_dialogueClientPipe.GetClientHandleAsString()}",
UseShellExecute = true, UseShellExecute = true,
RedirectStandardOutput = false, RedirectStandardOutput = false,
}; };
} }
internal static void PostMessage( internal static void PostMessage(
string instruction, string instruction,
params string[] operands params string[] operands
) )
{ {
CreateDialogueProcessIfNotExist(); CreateDialogueProcessIfNotExist();
_dw.WriteLine($"{instruction} {operands}"); _dw.WriteLine($"{instruction} {operands}");
} }
internal static void UnloadDialogue() internal static void UnloadDialogue()
{ {
if (_dialogueProcess != null) { if (_dialogueProcess != null) {
_dialogueProcess.Kill(); _dialogueProcess.Kill();
} }
_dialogueProcess = null; _dialogueProcess = null;
_dialogueServerPipe.Dispose(); _dialogueServerPipe.Dispose();
_dialogueClientPipe.Dispose(); _dialogueClientPipe.Dispose();
} }
internal static string WaitCertainReturn( internal static string WaitCertainReturn(
string target string target
) )
{ {
if (_dialogueProcess == null) { if (_dialogueProcess == null) {
return string.Empty; return string.Empty;
} }
string temp = string .Empty; string temp = string .Empty;
do do
{ {
temp = _dr.ReadLine(); temp = _dr.ReadLine();
temp = temp ?? string.Empty; temp = temp ?? string.Empty;
if (!temp!.StartsWith(target)) if (!temp!.StartsWith(target))
{ {
break; break;
} }
} while (true); } while (true);
int index = temp.IndexOf(' '); int index = temp.IndexOf(' ');
return index != -1 ? temp.Substring(index + 1) : string.Empty; return index != -1 ? temp.Substring(index + 1) : string.Empty;
} }
private static void AssertThread() private static void AssertThread()
{ {
if (_readAndWriteThreadId != Environment.CurrentManagedThreadId) { if (_readAndWriteThreadId != Environment.CurrentManagedThreadId) {
throw new InvalidOperationException( throw new InvalidOperationException(
"Communication with dialogue can only call from certain thread."); "Communication with dialogue can only call from certain thread.");
} }
} }
private static void DialogueProcessExitCallback( private static void DialogueProcessExitCallback(
object? sender, object? sender,
EventArgs e EventArgs e
) )
{ {
_dialogueProcess = null; _dialogueProcess = null;
_dr.Close(); _dr.Close();
_dw.Close(); _dw.Close();
} }
} }
} }
+44 -44
View File
@@ -1,44 +1,44 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="ErrorDefine" // file="ErrorDefine"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp namespace StgSharp
{ {
public class UniformEmptyReferenceException : Exception public class UniformEmptyReferenceException : Exception
{ {
public UniformEmptyReferenceException(string program, string name) public UniformEmptyReferenceException(string program, string name)
: base( : base(
$"Location -1 returned when trying to find uniform {name} in shader program {program}") { } $"Location -1 returned when trying to find uniform {name} in shader program {program}") { }
} }
} }
+97 -97
View File
@@ -1,98 +1,98 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="GlobalSetting.cs" // file="GlobalSetting.cs"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person // Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation // obtaining a copy of this software and associated documentation
// files (the “Software”), to deal in the Software without restriction, // files (the “Software”), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, // including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, // publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so, // and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions: // subject to the following conditions:
// //
// The above copyright notice and // The above copyright notice and
// this permission notice shall be included in all copies // this permission notice shall be included in all copies
// or substantial portions of the Software. // or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED “AS IS”, // THE SOFTWARE IS PROVIDED “AS IS”,
// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // 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, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH // ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp namespace StgSharp
{ {
public static unsafe partial class World public static unsafe partial class World
{ {
internal const int ssdSegmentLength = 16; internal const int ssdSegmentLength = 16;
private static byte[] _currentHash = []; private static byte[] _currentHash = [];
private static bool _inited; private static bool _inited;
public static byte[] CurrentAssemblyHash public static byte[] CurrentAssemblyHash
{ {
get get
{ {
if (_currentHash == null) if (_currentHash == null)
{ {
string route = Assembly.GetExecutingAssembly().Location; string route = Assembly.GetExecutingAssembly().Location;
string time = DateTime.UtcNow.ToString(); string time = DateTime.UtcNow.ToString();
using (SHA256 sh = SHA256.Create()) using (SHA256 sh = SHA256.Create())
{ {
byte[] bytes = Encoding.UTF8.GetBytes(route); byte[] bytes = Encoding.UTF8.GetBytes(route);
_currentHash = sh.ComputeHash(bytes); _currentHash = sh.ComputeHash(bytes);
} }
} }
return _currentHash; return _currentHash;
} }
} }
public static int MainThreadID public static int MainThreadID
{ {
get => (_mainThreadID == -1) ? get => (_mainThreadID == -1) ?
throw new InvalidOperationException( throw new InvalidOperationException(
"StgSharp environment is not inited. Main thread id is not available") : "StgSharp environment is not inited. Main thread id is not available") :
_mainThreadID; _mainThreadID;
internal set => _mainThreadID = value; internal set => _mainThreadID = value;
} }
private static int _mainThreadID { get; set; } = -1; private static int _mainThreadID { get; set; } = -1;
public static class GlobalSetting public static class GlobalSetting
{ {
private static bool vSyncActivated; private static bool vSyncActivated;
public static bool VSyncActivated public static bool VSyncActivated
{ {
get => vsyncActivated; get => vsyncActivated;
set set
{ {
// GraphicFramework.glfwSwapInterval(value ? 1 : 0); // GraphicFramework.glfwSwapInterval(value ? 1 : 0);
vsyncActivated = value; vsyncActivated = value;
} }
} }
} }
}//-------------------------------------- End of Class ---------------------------------------// }//-------------------------------------- End of Class ---------------------------------------//
} }
+283 -283
View File
@@ -1,284 +1,284 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="IStaticModule" // file="IStaticModule"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
namespace StgSharp namespace StgSharp
{ {
/// <summary> /// <summary>
/// Represents a statically activated module that can be registered and initialized as part of /// Represents a statically activated module that can be registered and initialized as part of
/// the application startup sequence, and gracefully torn down on shutdown. /// the application startup sequence, and gracefully torn down on shutdown.
/// </summary> /// </summary>
public interface IStaticModule public interface IStaticModule
{ {
/// <summary> /// <summary>
/// Gets the display name of this module, used for logging and diagnostics. /// Gets the display name of this module, used for logging and diagnostics.
/// </summary> /// </summary>
string ModuleName { get; } string ModuleName { get; }
/// <summary> /// <summary>
/// Performs all initialization work for this module using the provided profile. /// Performs all initialization work for this module using the provided profile.
/// </summary> /// </summary>
/// <param name="profile"> /// <param name="profile">
/// The configuration profile supplied at registration time. Use <see /// The configuration profile supplied at registration time. Use <see
/// cref="IModuleInitializeProfile.Empty" /> if no configuration is required. /// cref="IModuleInitializeProfile.Empty" /> if no configuration is required.
/// </param> /// </param>
void InitializeModule(IModuleInitializeProfile profile); void InitializeModule(IModuleInitializeProfile profile);
/// <summary> /// <summary>
/// Releases all resources acquired during <see cref="InitializeModule" />. Implementations should /// Releases all resources acquired during <see cref="InitializeModule" />. Implementations should
/// be idempotent: calling this method more than once must not throw. /// be idempotent: calling this method more than once must not throw.
/// </summary> /// </summary>
void UninitializeModule(); void UninitializeModule();
} }
/// <summary> /// <summary>
/// Marker interface for module initialization configuration. Implementations are plain data /// Marker interface for module initialization configuration. Implementations are plain data
/// carriers; no static factory is required. /// carriers; no static factory is required.
/// </summary> /// </summary>
public interface IModuleInitializeProfile public interface IModuleInitializeProfile
{ {
/// <summary> /// <summary>
/// A shared, allocation-free empty profile for modules that require no configuration. /// A shared, allocation-free empty profile for modules that require no configuration.
/// </summary> /// </summary>
static readonly IModuleInitializeProfile Empty = EmptyModuleInitializeProfile.Instance; static readonly IModuleInitializeProfile Empty = EmptyModuleInitializeProfile.Instance;
private sealed class EmptyModuleInitializeProfile : IModuleInitializeProfile private sealed class EmptyModuleInitializeProfile : IModuleInitializeProfile
{ {
public static readonly EmptyModuleInitializeProfile Instance = new(); public static readonly EmptyModuleInitializeProfile Instance = new();
private EmptyModuleInitializeProfile() { } private EmptyModuleInitializeProfile() { }
} }
} }
/// <summary> /// <summary>
/// Pairs an <see cref="IStaticModule" /> with its <see cref="IModuleInitializeProfile" />, /// Pairs an <see cref="IStaticModule" /> with its <see cref="IModuleInitializeProfile" />,
/// ensuring the two are never stored or iterated independently. /// ensuring the two are never stored or iterated independently.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Using a <see langword="readonly struct" /> avoids the boxing overhead that would occur if /// 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. /// the pair were stored as two parallel lists of reference types.
/// </remarks> /// </remarks>
public readonly struct ModuleDescriptor public readonly struct ModuleDescriptor
{ {
/// <summary> /// <summary>
/// Initializes a new <see cref="ModuleDescriptor" /> using <see /// Initializes a new <see cref="ModuleDescriptor" /> using <see
/// cref="IModuleInitializeProfile.Empty" /> as the profile. /// cref="IModuleInitializeProfile.Empty" /> as the profile.
/// </summary> /// </summary>
public ModuleDescriptor(IStaticModule module) : this(module, IModuleInitializeProfile.Empty) { } public ModuleDescriptor(IStaticModule module) : this(module, IModuleInitializeProfile.Empty) { }
/// <summary> /// <summary>
/// Initializes a new <see cref="ModuleDescriptor" /> with an explicit profile. /// Initializes a new <see cref="ModuleDescriptor" /> with an explicit profile.
/// </summary> /// </summary>
public ModuleDescriptor(IStaticModule module, IModuleInitializeProfile profile) public ModuleDescriptor(IStaticModule module, IModuleInitializeProfile profile)
{ {
Module = module; Module = module;
Profile = profile; Profile = profile;
} }
/// <summary> /// <summary>
/// Gets the configuration profile associated with the module. /// Gets the configuration profile associated with the module.
/// </summary> /// </summary>
public IModuleInitializeProfile Profile { get; } public IModuleInitializeProfile Profile { get; }
/// <summary> /// <summary>
/// Gets the module to be initialized. /// Gets the module to be initialized.
/// </summary> /// </summary>
public IStaticModule Module { get; } public IStaticModule Module { get; }
/// <summary> /// <summary>
/// Calls <see cref="IStaticModule.InitializeModule" /> with the stored profile. /// Calls <see cref="IStaticModule.InitializeModule" /> with the stored profile.
/// </summary> /// </summary>
public void Initialize() public void Initialize()
{ {
Module.InitializeModule(Profile); Module.InitializeModule(Profile);
} }
/// <summary> /// <summary>
/// Calls <see cref="IStaticModule.UninitializeModule" /> on the stored module. /// Calls <see cref="IStaticModule.UninitializeModule" /> on the stored module.
/// </summary> /// </summary>
public void Uninitialize() public void Uninitialize()
{ {
Module.UninitializeModule(); Module.UninitializeModule();
} }
} }
/// <summary> /// <summary>
/// An ordered collection of modules to be initialized during application startup and /// 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> /// uninitialized on shutdown. Supports three registration strategies so that the <c>new()</c>
/// generic constraint is never a hard requirement. /// generic constraint is never a hard requirement.
/// </summary> /// </summary>
public sealed class ModuleToInitializeCollection : IEnumerable<IStaticModule>, IStaticModule public sealed class ModuleToInitializeCollection : IEnumerable<IStaticModule>, IStaticModule
{ {
private readonly List<ModuleDescriptor> _descriptors = []; private readonly List<ModuleDescriptor> _descriptors = [];
internal ModuleToInitializeCollection(World.Initializer i) internal ModuleToInitializeCollection(World.Initializer i)
{ {
_descriptors.Add(new ModuleDescriptor(i)); _descriptors.Add(new ModuleDescriptor(i));
} }
// ── IEnumerable<IStaticModule> ─────────────────────────────────────────── // ── IEnumerable<IStaticModule> ───────────────────────────────────────────
/// <inheritdoc /> /// <inheritdoc />
public IEnumerator<IStaticModule> GetEnumerator() public IEnumerator<IStaticModule> GetEnumerator()
{ {
foreach (ModuleDescriptor d in _descriptors) { foreach (ModuleDescriptor d in _descriptors) {
yield return d.Module; yield return d.Module;
} }
} }
// ── Initialization / Uninitialization ──────────────────────────────────── // ── Initialization / Uninitialization ────────────────────────────────────
/// <summary> /// <summary>
/// Initializes all registered modules in the order they were registered. Each module /// Initializes all registered modules in the order they were registered. Each module
/// receives the profile that was supplied at registration time. /// receives the profile that was supplied at registration time.
/// </summary> /// </summary>
public void InitializeModule() public void InitializeModule()
{ {
for (int i = 0; i < _descriptors.Count; i++) { for (int i = 0; i < _descriptors.Count; i++) {
_descriptors[i].Initialize(); _descriptors[i].Initialize();
} }
} }
/// <summary> /// <summary>
/// Uninitializes all registered modules in <b> reverse </b> registration order, mirroring /// 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 /// the typical LIFO teardown convention (e.g., GLFW must be unloaded after all subsystems
/// that depend on it). /// that depend on it).
/// </summary> /// </summary>
public void UninitializeModule() public void UninitializeModule()
{ {
for (int i = _descriptors.Count - 1; i >= 0; i--) { for (int i = _descriptors.Count - 1; i >= 0; i--) {
_descriptors[i].Uninitialize(); _descriptors[i].Uninitialize();
} }
} }
// ── Registration API ───────────────────────────────────────────────────── // ── Registration API ─────────────────────────────────────────────────────
/// <summary> /// <summary>
/// Registers a module of type <typeparamref name="T" /> using its default constructor. /// 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 /// This is the most concise overload for simple modules that expose a public
/// parameterless constructor. /// parameterless constructor.
/// </summary> /// </summary>
/// <typeparam name="T"> /// <typeparam name="T">
/// A concrete module type that implements <see cref="IStaticModule" /> and exposes a /// A concrete module type that implements <see cref="IStaticModule" /> and exposes a
/// public parameterless constructor. /// public parameterless constructor.
/// </typeparam> /// </typeparam>
/// <param name="profile"> /// <param name="profile">
/// Optional initialization profile. Defaults to <see /// Optional initialization profile. Defaults to <see
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />. /// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
/// </param> /// </param>
public ModuleToInitializeCollection UseModule<T>(IModuleInitializeProfile? profile = null) public ModuleToInitializeCollection UseModule<T>(IModuleInitializeProfile? profile = null)
where T: IStaticModule, new() where T: IStaticModule, new()
{ {
return UseModule(static() => new T(), profile); return UseModule(static() => new T(), profile);
} }
/// <summary> /// <summary>
/// Registers a module produced by a factory delegate. This overload imposes no generic /// Registers a module produced by a factory delegate. This overload imposes no generic
/// constraints and supports any construction strategy, including dependency injection and /// constraints and supports any construction strategy, including dependency injection and
/// parameterized constructors. /// parameterized constructors.
/// </summary> /// </summary>
/// <param name="factory"> /// <param name="factory">
/// A delegate that constructs and returns the module instance. The delegate is invoked /// A delegate that constructs and returns the module instance. The delegate is invoked
/// once, immediately at registration time. If <paramref name="factory" /> is <see /// once, immediately at registration time. If <paramref name="factory" /> is <see
/// langword="null" /> or returns <see langword="null" />, the call is silently ignored /// langword="null" /> or returns <see langword="null" />, the call is silently ignored
/// and the collection is returned unchanged. /// and the collection is returned unchanged.
/// </param> /// </param>
/// <param name="profile"> /// <param name="profile">
/// Optional initialization profile. Defaults to <see /// Optional initialization profile. Defaults to <see
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />. /// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
/// </param> /// </param>
public ModuleToInitializeCollection UseModule( public ModuleToInitializeCollection UseModule(
Func<IStaticModule> factory, Func<IStaticModule> factory,
IModuleInitializeProfile? profile = null) IModuleInitializeProfile? profile = null)
{ {
if (factory is null) { if (factory is null) {
return this; return this;
} }
IStaticModule? mod = factory(); IStaticModule? mod = factory();
if (mod is null) { if (mod is null) {
return this; return this;
} }
_descriptors.Add(new ModuleDescriptor( _descriptors.Add(new ModuleDescriptor(
mod, mod,
profile ?? IModuleInitializeProfile.Empty)); profile ?? IModuleInitializeProfile.Empty));
return this; return this;
} }
/// <summary> /// <summary>
/// Registers a pre-existing module instance. Use this overload when the module is /// 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. /// provided externally, such as from a DI container, an object pool, or a singleton.
/// </summary> /// </summary>
/// <param name="module"> /// <param name="module">
/// The module instance to register. /// The module instance to register.
/// </param> /// </param>
/// <param name="profile"> /// <param name="profile">
/// Optional initialization profile. Defaults to <see /// Optional initialization profile. Defaults to <see
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />. /// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
/// </param> /// </param>
public ModuleToInitializeCollection UseModule(IStaticModule module, IModuleInitializeProfile? profile = null) public ModuleToInitializeCollection UseModule(IStaticModule module, IModuleInitializeProfile? profile = null)
{ {
_descriptors.Add(new ModuleDescriptor( _descriptors.Add(new ModuleDescriptor(
module, module,
profile ?? IModuleInitializeProfile.Empty)); profile ?? IModuleInitializeProfile.Empty));
return this; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
return GetEnumerator(); return GetEnumerator();
} }
// ── IStaticModule explicit implementation ──────────────────────────────── // ── IStaticModule explicit implementation ────────────────────────────────
/// <summary> /// <summary>
/// Explicit <see cref="IStaticModule" /> implementation. Delegates to <see /// Explicit <see cref="IStaticModule" /> implementation. Delegates to <see
/// cref="InitializeModule()" />, ignoring the supplied profile because each contained module uses /// cref="InitializeModule()" />, ignoring the supplied profile because each contained module uses
/// its own registered profile. /// its own registered profile.
/// </summary> /// </summary>
void IStaticModule.InitializeModule(IModuleInitializeProfile profile) void IStaticModule.InitializeModule(IModuleInitializeProfile profile)
{ {
InitializeModule(); InitializeModule();
} }
/// <inheritdoc /> /// <inheritdoc />
string IStaticModule.ModuleName => "Module Initializer"; string IStaticModule.ModuleName => "Module Initializer";
} }
} }
+236 -236
View File
@@ -1,237 +1,237 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="Platform" // file="Platform"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using StgSharp.Mathematics; using StgSharp.Mathematics;
using System; using System;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace StgSharp namespace StgSharp
{ {
internal enum GraphicAPI : byte internal enum GraphicAPI : byte
{ {
GL, GL,
VK VK
} }
public static partial class World public static partial class World
{ {
/// <summary> /// <summary>
/// Version of current World platform. Main version represent in hundred's digit, and sub /// Version of current World platform. Main version represent in hundred's digit, and sub
/// version represent in ten's digit. /// version represent in ten's digit.
/// </summary> /// </summary>
public const long version = 430; public const long version = 430;
private static bool vsyncActivated; private static bool vsyncActivated;
private static uint markCount = 0; private static uint markCount = 0;
internal static GraphicAPI API = default; internal static GraphicAPI API = default;
public static ModuleToInitializeCollection Initialize() public static ModuleToInitializeCollection Initialize()
{ {
return new ModuleToInitializeCollection(new Initializer()); return new ModuleToInitializeCollection(new Initializer());
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void LogError( public static void LogError(
Exception ex Exception ex
) )
{ {
DefaultLog.InternalWriteLog(ex.Message, LogType.Error); DefaultLog.InternalWriteLog(ex.Message, LogType.Error);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void LogInfo( public static void LogInfo(
string log string log
) )
{ {
DefaultLog.InternalWriteLog(log, LogType.Info); DefaultLog.InternalWriteLog(log, LogType.Info);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void LogWarning( public static void LogWarning(
string log string log
) )
{ {
DefaultLog.InternalWriteLog(log, LogType.Warning); DefaultLog.InternalWriteLog(log, LogType.Warning);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void LogWarning( public static void LogWarning(
Exception notVerySeriesException Exception notVerySeriesException
) )
{ {
DefaultLog.InternalWriteLog(notVerySeriesException.Message, LogType.Warning); DefaultLog.InternalWriteLog(notVerySeriesException.Message, LogType.Warning);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Mark( public static void Mark(
bool needConsole bool needConsole
) )
{ {
markCount++; markCount++;
if (needConsole) { if (needConsole) {
Console.WriteLine(markCount); Console.WriteLine(markCount);
} }
} }
public static void Terminate( public static void Terminate(
int exitCode int exitCode
) )
{ {
MainTimeProvider.StopProvidingTime(); MainTimeProvider.StopProvidingTime();
// GraphicFramework.glfwTerminate(); // GraphicFramework.glfwTerminate();
Environment.Exit(exitCode); Environment.Exit(exitCode);
} }
public static void Track( public static void Track(
object target, object target,
string message, string message,
bool isConsole, bool isConsole,
[CallerFilePath] string filePath = "", [CallerFilePath] string filePath = "",
[CallerMemberName] string callerName = "", [CallerMemberName] string callerName = "",
[CallerLineNumber] int lineNumber = 0 [CallerLineNumber] int lineNumber = 0
) )
{ {
Type t = target.GetType(); Type t = target.GetType();
string log = $"Tracked object: {nameof(target)}\t\tType:{t.Name}\n"; string log = $"Tracked object: {nameof(target)}\t\tType:{t.Name}\n";
FieldInfo[] fieldInfo = t.GetFields( FieldInfo[] fieldInfo = t.GetFields(
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
log += "|\tFieldName\t|\tFieldType\t|\tFieldValue\t|\n"; log += "|\tFieldName\t|\tFieldType\t|\tFieldValue\t|\n";
foreach (FieldInfo field in fieldInfo) { foreach (FieldInfo field in fieldInfo) {
log += $"|{field.Name}\t\t|{field.FieldType}\t\t|{field.GetValue(target)}\t\t|\n"; 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 += $"Tracking at:\n{filePath}\t{callerName}\tline {lineNumber}\n";
log += $"Additional information:\n{message}\n"; log += $"Additional information:\n{message}\n";
if (isConsole) if (isConsole)
{ {
Console.WriteLine(log); Console.WriteLine(log);
} else } else
{ {
DefaultLog.InternalWriteLog(log, LogType.Info); DefaultLog.InternalWriteLog(log, LogType.Info);
} }
} }
/// <summary> /// <summary>
/// Write a log message to log file, and mark the _level of this message. /// Write a log message to log file, and mark the _level of this message.
/// </summary> /// </summary>
/// <param _label="message"> /// <param _label="message">
/// A string contains information of how current evironment runs. /// A string contains information of how current evironment runs.
/// </param> /// </param>
/// <param _label="logType"> /// <param _label="logType">
/// The severity of current message. /// The severity of current message.
/// </param> /// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteLog( public static void WriteLog(
string message, string message,
LogType logType LogType logType
) )
{ {
DefaultLog.InternalWriteLog(message, logType); DefaultLog.InternalWriteLog(message, logType);
} }
internal static TDele ConvertAPI<TDele>( internal static TDele ConvertAPI<TDele>(
IntPtr funcPtr IntPtr funcPtr
) )
{ {
if (funcPtr == IntPtr.Zero) { if (funcPtr == IntPtr.Zero) {
throw new InvalidCastException( throw new InvalidCastException(
"Failed to convert api, the function pointer is zero."); "Failed to convert api, the function pointer is zero.");
} }
try try
{ {
return Marshal.GetDelegateForFunctionPointer<TDele>(funcPtr); return Marshal.GetDelegateForFunctionPointer<TDele>(funcPtr);
} }
catch (Exception) catch (Exception)
{ {
throw; throw;
} }
} }
internal static Delegate ConvertAPI( internal static Delegate ConvertAPI(
IntPtr funcPtr, IntPtr funcPtr,
Type T Type T
) )
{ {
if (funcPtr == IntPtr.Zero) { if (funcPtr == IntPtr.Zero) {
throw new InvalidCastException( throw new InvalidCastException(
"Failed to convert api, the function pointer is zero."); "Failed to convert api, the function pointer is zero.");
} }
try try
{ {
return Marshal.GetDelegateForFunctionPointer(funcPtr, T); return Marshal.GetDelegateForFunctionPointer(funcPtr, T);
} }
catch (Exception) catch (Exception)
{ {
throw; throw;
} }
} }
internal static unsafe void InternalInitialize() internal static unsafe void InternalInitialize()
{ {
// Initialize time provider and start providing time // Initialize time provider and start providing time
MainTimeProvider.StartProvidingTime(); MainTimeProvider.StartProvidingTime();
DefaultLog.InternalAppendLog("\n\n\n"); DefaultLog.InternalAppendLog("\n\n\n");
DefaultLog.InternalWriteLog( DefaultLog.InternalWriteLog(
$"Program {Assembly.GetEntryAssembly()!.FullName} on {Environment.MachineName} Started.", LogType.Info); $"Program {Assembly.GetEntryAssembly()!.FullName} on {Environment.MachineName} Started.", LogType.Info);
MainThreadID = System.Environment.CurrentManagedThreadId; MainThreadID = System.Environment.CurrentManagedThreadId;
/**/ /**/
Dialogue.NeedShowWhenStartup = false; Dialogue.NeedShowWhenStartup = false;
Dialogue.LoadDialogue(); Dialogue.LoadDialogue();
if (Dialogue.NeedShowWhenStartup) { if (Dialogue.NeedShowWhenStartup) {
Dialogue.CreateDialogueProcessIfNotExist(); Dialogue.CreateDialogueProcessIfNotExist();
} }
/**/ /**/
} }
internal class Initializer : IStaticModule internal class Initializer : IStaticModule
{ {
public string ModuleName => "StgSharpCore"; public string ModuleName => "StgSharpCore";
public void InitializeModule( public void InitializeModule(
IModuleInitializeProfile profile IModuleInitializeProfile profile
) )
{ {
World.InternalInitialize(); World.InternalInitialize();
} }
public void UninitializeModule() public void UninitializeModule()
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
} }
} }
+37 -37
View File
@@ -1,37 +1,37 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="Global" // file="Global"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
global using StgSharp.Internal; global using StgSharp.Internal;
global global
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("StgSharp.Mathematics")] [assembly: InternalsVisibleTo("StgSharp.Mathematics")]
[assembly: InternalsVisibleTo("StgSharp.RegularAnalysis")] [assembly: InternalsVisibleTo("StgSharp.RegularAnalysis")]
[assembly: InternalsVisibleTo("StgSharp.Graphics")] [assembly: InternalsVisibleTo("StgSharp.Graphics")]
[assembly: InternalsVisibleTo("StgSharpDebug")] [assembly: InternalsVisibleTo("StgSharpDebug")]
@@ -1,163 +1,163 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="BufferStack" // file="BufferStack"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace StgSharp.Mathematics.Memory namespace StgSharp.Mathematics.Memory
{ {
[CollectionBuilder(typeof(BufferStackBuilder), nameof(BufferStackBuilder.Create))] [CollectionBuilder(typeof(BufferStackBuilder), nameof(BufferStackBuilder.Create))]
public unsafe class BufferStack<T> : IDisposable where T: unmanaged public unsafe class BufferStack<T> : IDisposable where T: unmanaged
{ {
private byte* _buffer; private byte* _buffer;
private ulong _count; private ulong _count;
public BufferStack(int capacity) public BufferStack(int capacity)
{ {
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(capacity); ArgumentOutOfRangeException.ThrowIfNegativeOrZero(capacity);
AllocateBuffer(capacity); AllocateBuffer(capacity);
} }
public int Count public int Count
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (int)_count; get => (int)_count;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() public void Clear()
{ {
((Header*)_buffer)->Top = 0; ((Header*)_buffer)->Top = 0;
_count = 0; _count = 0;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose() public void Dispose()
{ {
Marshal.FreeHGlobal((nint)_buffer); Marshal.FreeHGlobal((nint)_buffer);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Pop() public T Pop()
{ {
Header* header = (Header*)_buffer; Header* header = (Header*)_buffer;
if (header->Top == 0) { if (header->Top == 0) {
throw new InvalidOperationException("Stack underflow"); throw new InvalidOperationException("Stack underflow");
} }
header->Top--; header->Top--;
_count--; _count--;
T* dataStart = (T*)(_buffer + sizeof(Header)); T* dataStart = (T*)(_buffer + sizeof(Header));
return dataStart[header->Top]; return dataStart[header->Top];
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Push(T item) public void Push(T item)
{ {
Header* header = (Header*)_buffer; Header* header = (Header*)_buffer;
if (header->Top >= header->Capacity) if (header->Top >= header->Capacity)
{ {
Expand(); Expand();
header = (Header*)_buffer; header = (Header*)_buffer;
} }
T* dataStart = (T*)(_buffer + sizeof(Header)); T* dataStart = (T*)(_buffer + sizeof(Header));
dataStart[header->Top] = item; dataStart[header->Top] = item;
header->Top++; header->Top++;
_count++; _count++;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryPop(out T value) public bool TryPop(out T value)
{ {
Header* header = (Header*)_buffer; Header* header = (Header*)_buffer;
if (header->Top == 0) if (header->Top == 0)
{ {
value = default; value = default;
return false; return false;
} }
header->Top--; header->Top--;
_count--; _count--;
T* dataStart = (T*)(_buffer + sizeof(Header)); T* dataStart = (T*)(_buffer + sizeof(Header));
value = dataStart[header->Top]; value = dataStart[header->Top];
return true; return true;
} }
// Internal method for efficient bulk initialization // Internal method for efficient bulk initialization
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal unsafe void InitializeFromSpan(ReadOnlySpan<T> span) internal unsafe void InitializeFromSpan(ReadOnlySpan<T> span)
{ {
if (span.IsEmpty) { if (span.IsEmpty) {
return; return;
} }
Header* header = (Header*)_buffer; Header* header = (Header*)_buffer;
T* dataStart = (T*)(_buffer + sizeof(Header)); T* dataStart = (T*)(_buffer + sizeof(Header));
fixed (T* sourcePtr = span) { fixed (T* sourcePtr = span) {
Buffer.MemoryCopy(sourcePtr, dataStart, header->Capacity * (nuint)sizeof(T), Buffer.MemoryCopy(sourcePtr, dataStart, header->Capacity * (nuint)sizeof(T),
(nuint)span.Length * (nuint)sizeof(T)); (nuint)span.Length * (nuint)sizeof(T));
} }
header->Top = (nuint)span.Length; header->Top = (nuint)span.Length;
_count = (ulong)span.Length; _count = (ulong)span.Length;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AllocateBuffer(int capacity) private void AllocateBuffer(int capacity)
{ {
nuint size = (nuint)sizeof(Header) + ((nuint)capacity * (nuint)sizeof(T)); nuint size = (nuint)sizeof(Header) + ((nuint)capacity * (nuint)sizeof(T));
_buffer = (byte*)NativeMemory.Alloc(size); _buffer = (byte*)NativeMemory.Alloc(size);
Header* header = (Header*)_buffer; Header* header = (Header*)_buffer;
header->Top = 0; header->Top = 0;
header->Capacity = (nuint)capacity; header->Capacity = (nuint)capacity;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Expand() private void Expand()
{ {
Header* header = (Header*)_buffer; Header* header = (Header*)_buffer;
nuint newCap = header->Capacity * 2; nuint newCap = header->Capacity * 2;
nuint newSize = (nuint)sizeof(Header) + (newCap * (nuint)sizeof(T)); nuint newSize = (nuint)sizeof(Header) + (newCap * (nuint)sizeof(T));
_buffer = (byte*)NativeMemory.Realloc(_buffer, newSize); _buffer = (byte*)NativeMemory.Realloc(_buffer, newSize);
header = (Header*)_buffer; header = (Header*)_buffer;
header->Capacity = newCap; header->Capacity = newCap;
} }
private struct Header private struct Header
{ {
public nuint Capacity; public nuint Capacity;
public nuint Top; public nuint Top;
} }
} }
} }
@@ -1,308 +1,308 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="ConcurrentBufferStack" // file="ConcurrentBufferStack"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using StgSharp.Threading; using StgSharp.Threading;
using System; using System;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
namespace StgSharp.Mathematics.Memory namespace StgSharp.Mathematics.Memory
{ {
public static class BufferStackBuilder public static class BufferStackBuilder
{ {
// For BufferStack collection builder // For BufferStack collection builder
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BufferStack<T> Create<T>(ReadOnlySpan<T> span) where T: unmanaged public static BufferStack<T> Create<T>(ReadOnlySpan<T> span) where T: unmanaged
{ {
if (span.IsEmpty) if (span.IsEmpty)
{ {
return new BufferStack<T>(4); // Default small capacity return new BufferStack<T>(4); // Default small capacity
} }
BufferStack<T> stack = new(span.Length); BufferStack<T> stack = new(span.Length);
// Use internal method for efficient bulk initialization // Use internal method for efficient bulk initialization
stack.InitializeFromSpan(span); stack.InitializeFromSpan(span);
return stack; return stack;
} }
// For ConcurrentBufferStack collection builder // For ConcurrentBufferStack collection builder
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ConcurrentBufferStack<T> CreateConcurrent<T>(ReadOnlySpan<T> span) where T: unmanaged public static ConcurrentBufferStack<T> CreateConcurrent<T>(ReadOnlySpan<T> span) where T: unmanaged
{ {
if (span.IsEmpty) if (span.IsEmpty)
{ {
return new ConcurrentBufferStack<T>(4); // Default small capacity return new ConcurrentBufferStack<T>(4); // Default small capacity
} }
ConcurrentBufferStack<T> stack = new(span.Length); ConcurrentBufferStack<T> stack = new(span.Length);
// Use internal method for efficient bulk initialization // Use internal method for efficient bulk initialization
stack.InitializeFromSpan(span); stack.InitializeFromSpan(span);
return stack; return stack;
} }
} }
[CollectionBuilder(builderType: typeof(BufferStackBuilder), methodName: nameof(BufferStackBuilder.CreateConcurrent))] [CollectionBuilder(builderType: typeof(BufferStackBuilder), methodName: nameof(BufferStackBuilder.CreateConcurrent))]
public unsafe class ConcurrentBufferStack<T> : IDisposable where T: unmanaged public unsafe class ConcurrentBufferStack<T> : IDisposable where T: unmanaged
{ {
private byte* _buffer; private byte* _buffer;
private readonly BufferExpansionLock _lock = new(); private readonly BufferExpansionLock _lock = new();
private long _count; private long _count;
public ConcurrentBufferStack() public ConcurrentBufferStack()
{ {
AllocateBuffer(4); AllocateBuffer(4);
} }
public ConcurrentBufferStack(int capacity) public ConcurrentBufferStack(int capacity)
{ {
if (capacity <= 0) { if (capacity <= 0) {
throw new ArgumentOutOfRangeException(nameof(capacity)); throw new ArgumentOutOfRangeException(nameof(capacity));
} }
AllocateBuffer(capacity); AllocateBuffer(capacity);
} }
public ulong Count public ulong Count
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (ulong)Volatile.Read(ref _count); get => (ulong)Volatile.Read(ref _count);
} }
public void Clear() public void Clear()
{ {
_lock.EnterExpansionProcess(); _lock.EnterExpansionProcess();
try try
{ {
_lock.EnterBufferCopy(); // exclusive while mutating Top _lock.EnterBufferCopy(); // exclusive while mutating Top
try try
{ {
Header* header = (Header*)_buffer; Header* header = (Header*)_buffer;
header->Top = 0; header->Top = 0;
Volatile.Write(ref _count, 0); Volatile.Write(ref _count, 0);
} }
finally finally
{ {
_lock.ExitBufferCopy(); _lock.ExitBufferCopy();
} }
} }
finally finally
{ {
_lock.ExitExpansionProcess(); _lock.ExitExpansionProcess();
} }
} }
public void Dispose() public void Dispose()
{ {
_lock.Dispose(); _lock.Dispose();
NativeMemory.Free(_buffer); NativeMemory.Free(_buffer);
} }
public T Pop() public T Pop()
{ {
while (true) while (true)
{ {
_lock.EnterBufferRead(); // prevent buffer being reallocated while reading/modifying Top _lock.EnterBufferRead(); // prevent buffer being reallocated while reading/modifying Top
try try
{ {
Header* header = (Header*)_buffer; Header* header = (Header*)_buffer;
int oldTop = Volatile.Read(ref header->Top); int oldTop = Volatile.Read(ref header->Top);
if (oldTop <= 0) { if (oldTop <= 0) {
throw new InvalidOperationException("Stack underflow"); throw new InvalidOperationException("Stack underflow");
} }
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop) if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
{ {
T* dataStart = (T*)(_buffer + sizeof(Header)); T* dataStart = (T*)(_buffer + sizeof(Header));
_ = Interlocked.Decrement(ref _count); _ = Interlocked.Decrement(ref _count);
return dataStart[oldTop - 1]; return dataStart[oldTop - 1];
} }
} }
finally finally
{ {
_lock.ExitBufferRead(); _lock.ExitBufferRead();
} }
Thread.Sleep(0); Thread.Sleep(0);
} }
} }
public void Push(T item) public void Push(T item)
{ {
while (true) while (true)
{ {
// Ensure capacity, expanding if needed // Ensure capacity, expanding if needed
_lock.EnterMetaDataRead(); _lock.EnterMetaDataRead();
try try
{ {
Header* header = (Header*)_buffer; Header* header = (Header*)_buffer;
int topSnapshot = Volatile.Read(ref header->Top); int topSnapshot = Volatile.Read(ref header->Top);
int cap = Volatile.Read(ref header->Capacity); int cap = Volatile.Read(ref header->Capacity);
if (topSnapshot >= cap) if (topSnapshot >= cap)
{ {
// Need expansion // Need expansion
_lock.ExitMetaDataRead(); _lock.ExitMetaDataRead();
_lock.EnterExpansionProcess(); _lock.EnterExpansionProcess();
try try
{ {
header = (Header*)_buffer; header = (Header*)_buffer;
if (header->Top >= header->Capacity) if (header->Top >= header->Capacity)
{ {
int newCap = header->Capacity * 2; int newCap = header->Capacity * 2;
nuint newSize = (nuint)sizeof(Header) + ((nuint)newCap * (nuint)sizeof(T)); nuint newSize = (nuint)sizeof(Header) + ((nuint)newCap * (nuint)sizeof(T));
_lock.EnterBufferCopy(); _lock.EnterBufferCopy();
try try
{ {
_buffer = (byte*)NativeMemory.Realloc(_buffer, newSize); _buffer = (byte*)NativeMemory.Realloc(_buffer, newSize);
((Header*)_buffer)->Capacity = newCap; ((Header*)_buffer)->Capacity = newCap;
} }
finally finally
{ {
_lock.ExitBufferCopy(); _lock.ExitBufferCopy();
} }
} }
} }
finally finally
{ {
_lock.ExitExpansionProcess(); _lock.ExitExpansionProcess();
} }
continue; continue;
} }
} }
finally finally
{ {
if (_lock.IsThreadReadingMetaData) { if (_lock.IsThreadReadingMetaData) {
_lock.ExitMetaDataRead(); _lock.ExitMetaDataRead();
} }
} }
// Reserve and write under exclusive buffer lock to avoid consumers reading unwritten data // Reserve and write under exclusive buffer lock to avoid consumers reading unwritten data
_lock.EnterBufferCopy(); _lock.EnterBufferCopy();
try try
{ {
Header* header = (Header*)_buffer; Header* header = (Header*)_buffer;
int idx = header->Top; // reserve next slot int idx = header->Top; // reserve next slot
if (idx >= header->Capacity) if (idx >= header->Capacity)
{ {
// lost race against expander or other producer; retry // lost race against expander or other producer; retry
continue; continue;
} }
T* dataStart = (T*)(_buffer + sizeof(Header)); T* dataStart = (T*)(_buffer + sizeof(Header));
dataStart[idx] = item; // write first dataStart[idx] = item; // write first
header->Top = idx + 1; // then publish by increasing top header->Top = idx + 1; // then publish by increasing top
_ = Interlocked.Increment(ref _count); _ = Interlocked.Increment(ref _count);
return; return;
} }
finally finally
{ {
_lock.ExitBufferCopy(); _lock.ExitBufferCopy();
} }
} }
} }
public bool TryPop(out T value) public bool TryPop(out T value)
{ {
while (true) while (true)
{ {
_lock.EnterBufferRead(); _lock.EnterBufferRead();
try try
{ {
Header* header = (Header*)_buffer; Header* header = (Header*)_buffer;
int oldTop = Volatile.Read(ref header->Top); int oldTop = Volatile.Read(ref header->Top);
if (oldTop <= 0) if (oldTop <= 0)
{ {
value = default; value = default;
return false; return false;
} }
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop) if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
{ {
T* dataStart = (T*)(_buffer + sizeof(Header)); T* dataStart = (T*)(_buffer + sizeof(Header));
value = dataStart[oldTop - 1]; value = dataStart[oldTop - 1];
_ = Interlocked.Decrement(ref _count); _ = Interlocked.Decrement(ref _count);
return true; return true;
} }
} }
finally finally
{ {
_lock.ExitBufferRead(); _lock.ExitBufferRead();
} }
Thread.Sleep(0); Thread.Sleep(0);
} }
} }
// Internal method for efficient bulk initialization // Internal method for efficient bulk initialization
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal unsafe void InitializeFromSpan(ReadOnlySpan<T> span) internal unsafe void InitializeFromSpan(ReadOnlySpan<T> span)
{ {
if (span.IsEmpty) { if (span.IsEmpty) {
return; return;
} }
Header* header = (Header*)_buffer; Header* header = (Header*)_buffer;
T* dataStart = (T*)(_buffer + sizeof(Header)); T* dataStart = (T*)(_buffer + sizeof(Header));
fixed (T* sourcePtr = span) { fixed (T* sourcePtr = span) {
Buffer.MemoryCopy(sourcePtr, dataStart, (nuint)header->Capacity * (nuint)sizeof(T), Buffer.MemoryCopy(sourcePtr, dataStart, (nuint)header->Capacity * (nuint)sizeof(T),
(nuint)span.Length * (nuint)sizeof(T)); (nuint)span.Length * (nuint)sizeof(T));
} }
header->Top = span.Length; header->Top = span.Length;
Volatile.Write(ref _count, span.Length); Volatile.Write(ref _count, span.Length);
} }
private void AllocateBuffer(int capacity) private void AllocateBuffer(int capacity)
{ {
nuint size = (nuint)sizeof(Header) + (nuint)capacity * (nuint)sizeof(T); nuint size = (nuint)sizeof(Header) + (nuint)capacity * (nuint)sizeof(T);
_buffer = (byte*)NativeMemory.Alloc(size); _buffer = (byte*)NativeMemory.Alloc(size);
Header* header = (Header*)_buffer; Header* header = (Header*)_buffer;
header->Top = 0; header->Top = 0;
header->Capacity = capacity; header->Capacity = capacity;
} }
private struct Header private struct Header
{ {
public int Capacity; // max capacity of the stack (number of elements) public int Capacity; // max capacity of the stack (number of elements)
public int Top; // next available index in the stack public int Top; // next available index in the stack
} }
} }
} }
@@ -1,114 +1,114 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="HLSFAllocationHandle" // file="HLSFAllocationHandle"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Allocator = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocator; using Allocator = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocator;
namespace StgSharp.Mathematics.Memory namespace StgSharp.Mathematics.Memory
{ {
public readonly unsafe struct HybridLayerSegregatedFitAllocationHandle public readonly unsafe struct HybridLayerSegregatedFitAllocationHandle
{ {
internal readonly Allocator.Entry* EntryHandle; internal readonly Allocator.Entry* EntryHandle;
public readonly long AllocSize; public readonly long AllocSize;
internal HybridLayerSegregatedFitAllocationHandle(Allocator.Entry* handle, long s) internal HybridLayerSegregatedFitAllocationHandle(Allocator.Entry* handle, long s)
{ {
EntryHandle = handle; EntryHandle = handle;
Pointer = (byte*)handle->Position; Pointer = (byte*)handle->Position;
AllocSize = s; AllocSize = s;
// Console.WriteLine($"{(ulong)handle->PreviousNear}<->{(ulong)handle->NextNear},\t{handle->Size}"); // Console.WriteLine($"{(ulong)handle->PreviousNear}<->{(ulong)handle->NextNear},\t{handle->Size}");
} }
public readonly ref byte this[uint index] public readonly ref byte this[uint index]
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get get
{ {
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, AllocSize); ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, AllocSize);
return ref Pointer[index]; return ref Pointer[index];
} }
} }
public readonly byte* Pointer { get; init; } public readonly byte* Pointer { get; init; }
public readonly Span<byte> BufferHandle public readonly Span<byte> BufferHandle
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(Pointer, (int)AllocSize); get => new(Pointer, (int)AllocSize);
} }
public readonly Enumerator GetEnumerator() public readonly Enumerator GetEnumerator()
{ {
return new Enumerator(this); return new Enumerator(this);
} }
public ref struct Enumerator : IEnumerator<byte> public ref struct Enumerator : IEnumerator<byte>
{ {
private HybridLayerSegregatedFitAllocationHandle _handle; private HybridLayerSegregatedFitAllocationHandle _handle;
private uint _index = ~0u; private uint _index = ~0u;
internal Enumerator(HybridLayerSegregatedFitAllocationHandle span) internal Enumerator(HybridLayerSegregatedFitAllocationHandle span)
{ {
_handle = span; _handle = span;
Span = span.BufferHandle; Span = span.BufferHandle;
} }
public ref byte RefCurrent => ref _handle[_index]; public ref byte RefCurrent => ref _handle[_index];
public Span<byte> Span { get; init; } public Span<byte> Span { get; init; }
public readonly void Dispose() { } public readonly void Dispose() { }
public bool MoveNext() public bool MoveNext()
{ {
_index++; _index++;
return _index < _handle.AllocSize; return _index < _handle.AllocSize;
} }
public void Reset() public void Reset()
{ {
_index = 0; _index = 0;
} }
object IEnumerator.Current => RefCurrent; object IEnumerator.Current => RefCurrent;
byte IEnumerator<byte>.Current => RefCurrent; byte IEnumerator<byte>.Current => RefCurrent;
} }
} }
} }
@@ -1,234 +1,234 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="HLSFAllocator.Alloc" // file="HLSFAllocator.Alloc"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Numerics; using System.Numerics;
using hlsfHandle = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocationHandle; using hlsfHandle = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocationHandle;
namespace StgSharp.Mathematics.Memory namespace StgSharp.Mathematics.Memory
{ {
public unsafe partial class HybridLayerSegregatedFitAllocator public unsafe partial class HybridLayerSegregatedFitAllocator
{ {
public hlsfHandle Alloc(long size) public hlsfHandle Alloc(long size)
{ {
if (size > _maxSize) { if (size > _maxSize) {
throw new InvalidOperationException("Requested size exceeds maximum allocatable size."); throw new InvalidOperationException("Requested size exceeds maximum allocatable size.");
} }
int level = GetLevelFromSize(size); int level = GetLevelFromSize(size);
int segmentIndex = DetermineSegmentIndex(_levelSizeArray, size, level); int segmentIndex = DetermineSegmentIndex(_levelSizeArray, size, level);
int i = level, s = segmentIndex; int i = level, s = segmentIndex;
Entry* handle; Entry* handle;
while (!TryPopLevelForAllocation(i, s, out handle)) while (!TryPopLevelForAllocation(i, s, out handle))
{ {
if (++s >= 3) if (++s >= 3)
{ {
s = 0; s = 0;
i++; i++;
} }
if (i > MaxLevel) if (i > MaxLevel)
{ {
if (TryPopLevelForAllocation(10, 1, out handle)) if (TryPopLevelForAllocation(10, 1, out handle))
{ {
break; break;
} else if (TryAllocateNew32MBBlock(out handle)) } else if (TryAllocateNew32MBBlock(out handle))
{ {
break; break;
} }
throw new OverflowException("Out of memory: Unable to allocate new 16MB block or find suitable bucket for allocation."); throw new OverflowException("Out of memory: Unable to allocate new 16MB block or find suitable bucket for allocation.");
} }
} }
if (handle != null) if (handle != null)
{ {
int requestedOffset = (segmentIndex + 1) * _levelSizeArray[level]; int requestedOffset = (segmentIndex + 1) * _levelSizeArray[level];
if (requestedOffset > handle->Size) { if (requestedOffset > handle->Size) {
throw new OverflowException("Out of memory: insufficient block size returned from spare memory."); throw new OverflowException("Out of memory: insufficient block size returned from spare memory.");
} }
long remainder = handle->Size - requestedOffset; long remainder = handle->Size - requestedOffset;
SetEntryLevel(handle, handle->Position, (uint)requestedOffset); SetEntryLevel(handle, handle->Position, (uint)requestedOffset);
handle->State = EntryState.Alloc; handle->State = EntryState.Alloc;
if (remainder > 0) { if (remainder > 0) {
SliceMemory(handle->NextNear, remainder); SliceMemory(handle->NextNear, remainder);
} }
} }
return new hlsfHandle(handle, size); return new hlsfHandle(handle, size);
} }
/// <summary> /// <summary>
/// Slice the memory block into smaller blocks and add them to the appropriate buckets /// 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 /// Notice: this method is not responsible for freeing the original BucketNode
/// </summary> /// </summary>
/// <param name="e"> /// <param name="e">
/// ///
/// </param> /// </param>
/// <param name="sizeToSlice"> /// <param name="sizeToSlice">
/// ///
/// </param> /// </param>
private void SliceMemory(Entry* e, long sizeToSlice) private void SliceMemory(Entry* e, long sizeToSlice)
{ {
Entry* next = e; Entry* next = e;
long remain = sizeToSlice; // remained size to slice long remain = sizeToSlice; // remained size to slice
long offset = (long)e->Position - sizeToSlice - (long)m_Buffer; // offset of buffer 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 long end = (long)e->Position - (long)m_Buffer; // end offset of buffer to slice
int i = (BitOperations.TrailingZeroCount(offset) - 6) / 2; int i = (BitOperations.TrailingZeroCount(offset) - 6) / 2;
i = int.Min(10, i); i = int.Min(10, i);
for (; i < _levelSizeArray.Length; i++) for (; i < _levelSizeArray.Length; i++)
{ {
int size = _levelSizeArray[i]; int size = _levelSizeArray[i];
int upper = size * 4; int upper = size * 4;
long edge = (offset + upper - 1) / upper * upper; long edge = (offset + upper - 1) / upper * upper;
int count = (int)((long.Min(edge, end) - offset) / size); int count = (int)((long.Min(edge, end) - offset) / size);
if (count > 0) if (count > 0)
{ {
long newSize = count * size; long newSize = count * size;
Entry* newEntry = (Entry*)_entries.Allocate(); Entry* newEntry = (Entry*)_entries.Allocate();
InsertEntryBefore(newEntry, next); InsertEntryBefore(newEntry, next);
SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize); SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
newEntry->State = EntryState.Empty; newEntry->State = EntryState.Empty;
BucketNode* b = (BucketNode*)newEntry->Position; BucketNode* b = (BucketNode*)newEntry->Position;
b->EntryRef = newEntry; b->EntryRef = newEntry;
PushBucketToLevel(i, count - 1, b); PushBucketToLevel(i, count - 1, b);
offset += newSize; offset += newSize;
remain -= newSize; remain -= newSize;
} }
if (remain <= 0) { if (remain <= 0) {
return; return;
} }
} }
if (i > MaxLevel) if (i > MaxLevel)
{ {
int size = _levelSizeArray[^1]; int size = _levelSizeArray[^1];
int upper = size * 4; int upper = size * 4;
long edge = end / upper * upper; long edge = end / upper * upper;
int count = (int)((long.Min(edge, end) - offset) / upper); int count = (int)((long.Min(edge, end) - offset) / upper);
if (count > 0) if (count > 0)
{ {
long newSize = (long)count * (long)upper; long newSize = (long)count * (long)upper;
Entry* newEntry = (Entry*)_entries.Allocate(); Entry* newEntry = (Entry*)_entries.Allocate();
InsertEntryBefore(newEntry, next); InsertEntryBefore(newEntry, next);
SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize); SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
newEntry->State = EntryState.Empty; newEntry->State = EntryState.Empty;
BucketNode* b = (BucketNode*)newEntry->Position; BucketNode* b = (BucketNode*)newEntry->Position;
b->EntryRef = newEntry; b->EntryRef = newEntry;
PushBucketToLevel(i, count - 1, b); PushBucketToLevel(i, count - 1, b);
offset += newSize; offset += newSize;
remain -= newSize; remain -= newSize;
if (remain <= 0) { if (remain <= 0) {
return; return;
} }
} }
} }
for (i--; i >= 0; i--) for (i--; i >= 0; i--)
{ {
int size = _levelSizeArray[i]; int size = _levelSizeArray[i];
long edge = end / size * size; long edge = end / size * size;
int count = (int)((long.Min(edge, end) - offset) / size); int count = (int)((long.Min(edge, end) - offset) / size);
if (count > 0) if (count > 0)
{ {
long newSize = count * size; long newSize = count * size;
Entry* newEntry = (Entry*)_entries.Allocate(); Entry* newEntry = (Entry*)_entries.Allocate();
InsertEntryBefore(newEntry, next); InsertEntryBefore(newEntry, next);
SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize); SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
newEntry->State = EntryState.Empty; newEntry->State = EntryState.Empty;
BucketNode* b = (BucketNode*)newEntry->Position; BucketNode* b = (BucketNode*)newEntry->Position;
b->EntryRef = newEntry; b->EntryRef = newEntry;
b->NextLevel = null; b->NextLevel = null;
b->PreviousLevel = null; b->PreviousLevel = null;
PushBucketToLevel(i, count - 1, b); PushBucketToLevel(i, count - 1, b);
offset += newSize; offset += newSize;
remain -= newSize; remain -= newSize;
} }
if (remain <= 0) { if (remain <= 0) {
return; return;
} }
} }
} }
/// <summary> /// <summary>
/// Try to allocate a new 16MB block when all buckets are empty /// Try to allocate a new 16MB block when all buckets are empty
/// </summary> /// </summary>
/// <param name="handle"> /// <param name="handle">
/// Output handle to the allocated Entry /// Output handle to the allocated Entry
/// </param> /// </param>
/// <returns> /// <returns>
/// True if allocation succeeded /// True if allocation succeeded
/// </returns> /// </returns>
private bool TryAllocateNew32MBBlock(out Entry* handle) private bool TryAllocateNew32MBBlock(out Entry* handle)
{ {
nuint pos = _spareMemory->Position; nuint pos = _spareMemory->Position;
long remain = _spareMemory->Size; long remain = _spareMemory->Size;
if (remain <= 0) if (remain <= 0)
{ {
handle = null; handle = null;
return false; return false;
} }
long size = remain > 32 * 1024 * 1024L ? 32 * 1024 * 1024L : remain; long size = remain > 32 * 1024 * 1024L ? 32 * 1024 * 1024L : remain;
// build new entry // build new entry
Entry* e = (Entry*)_entries.Allocate(); Entry* e = (Entry*)_entries.Allocate();
SetEntryLevel(e, pos, size); SetEntryLevel(e, pos, size);
InsertEntryBefore(e, _spareMemory); InsertEntryBefore(e, _spareMemory);
e->State = EntryState.Empty; e->State = EntryState.Empty;
// set fields of spare memory // set fields of spare memory
_spareMemory->Size -= size; _spareMemory->Size -= size;
_spareMemory->Position += (nuint)size; _spareMemory->Position += (nuint)size;
handle = e; handle = e;
return true; return true;
} }
/// <summary> /// <summary>
/// Helper method for allocation - converts BucketNode to Entry /// Helper method for allocation - converts BucketNode to Entry
/// </summary> /// </summary>
private bool TryPopLevelForAllocation(int levelIndex, int sizeIndex, out Entry* entry) private bool TryPopLevelForAllocation(int levelIndex, int sizeIndex, out Entry* entry)
{ {
if (TryPopLevel(levelIndex, sizeIndex, out BucketNode* bucketNode)) if (TryPopLevel(levelIndex, sizeIndex, out BucketNode* bucketNode))
{ {
entry = bucketNode->EntryRef; entry = bucketNode->EntryRef;
return entry != null; return entry != null;
} }
entry = null; entry = null;
return false; return false;
} }
} }
} }
@@ -1,171 +1,171 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="HLSFAllocator.BucketNode" // file="HLSFAllocator.BucketNode"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace StgSharp.Mathematics.Memory namespace StgSharp.Mathematics.Memory
{ {
public unsafe partial class HybridLayerSegregatedFitAllocator public unsafe partial class HybridLayerSegregatedFitAllocator
{ {
private void PushBucketToLevel(int levelIndex, int sizeIndex, BucketNode* node) private void PushBucketToLevel(int levelIndex, int sizeIndex, BucketNode* node)
{ {
if (node == null || node->EntryRef == null) { if (node == null || node->EntryRef == null) {
return; return;
} }
Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex; Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex;
if (_bucketHeads[index] is null) if (_bucketHeads[index] is null)
{ {
_bucketHeads[index] = node; _bucketHeads[index] = node;
node->IsInBucket = true; node->IsInBucket = true;
node->NextLevel = null; node->NextLevel = null;
node->PreviousLevel = null; node->PreviousLevel = null;
// Console.WriteLine($"Push to empty level of {levelIndex} {sizeIndex}, bucket pos {(ulong)node}"); // Console.WriteLine($"Push to empty level of {levelIndex} {sizeIndex}, bucket pos {(ulong)node}");
return; return;
} else } else
{ {
BucketNode* head = _bucketHeads[index]; BucketNode* head = _bucketHeads[index];
node->NextLevel = head; node->NextLevel = head;
BucketNode* headNode = head; BucketNode* headNode = head;
node->IsInBucket = true; node->IsInBucket = true;
_bucketHeads[index] = node; _bucketHeads[index] = node;
// Console.WriteLine($"Push to head of level of {levelIndex} {sizeIndex}, bucket pos {(ulong)node}, previous head is at {head}"); // Console.WriteLine($"Push to head of level of {levelIndex} {sizeIndex}, bucket pos {(ulong)node}, previous head is at {head}");
if (headNode != null) if (headNode != null)
{ {
headNode->PreviousLevel = node; headNode->PreviousLevel = node;
} }
return; return;
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void RemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node) private void RemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
{ {
Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex; Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex;
if (_bucketHeads[index] == node) if (_bucketHeads[index] == node)
{ {
if (TryPopLevel(levelIndex, sizeIndex, out _)) { if (TryPopLevel(levelIndex, sizeIndex, out _)) {
return; return;
} }
} }
BucketNode* previous = node->PreviousLevel; BucketNode* previous = node->PreviousLevel;
BucketNode* next = node->NextLevel; BucketNode* next = node->NextLevel;
if (previous != null) { if (previous != null) {
previous->NextLevel = next; previous->NextLevel = next;
} }
if (next != null) { if (next != null) {
next->PreviousLevel = previous; next->PreviousLevel = previous;
} }
node->NextLevel = (BucketNode*)null; node->NextLevel = (BucketNode*)null;
node->PreviousLevel = (BucketNode*)null; node->PreviousLevel = (BucketNode*)null;
node->IsInBucket = false; node->IsInBucket = false;
return; return;
} }
private bool TryPopLevel(int levelIndex, int sizeIndex, out BucketNode* node) private bool TryPopLevel(int levelIndex, int sizeIndex, out BucketNode* node)
{ {
Index index = levelIndex > MaxLevel ? ^1 : (3 * levelIndex) + sizeIndex; Index index = levelIndex > MaxLevel ? ^1 : (3 * levelIndex) + sizeIndex;
BucketNode* head = _bucketHeads[index]; BucketNode* head = _bucketHeads[index];
if (head == null) if (head == null)
{ {
node = null; node = null;
return false; return false;
} }
// Console.WriteLine($"level popped from level {levelIndex} {sizeIndex}, node is at {(ulong)head}"); // Console.WriteLine($"level popped from level {levelIndex} {sizeIndex}, node is at {(ulong)head}");
BucketNode* next = head->NextLevel; BucketNode* next = head->NextLevel;
if (next == null) if (next == null)
{ {
_bucketHeads[index] = null; _bucketHeads[index] = null;
node = head; node = head;
return true; return true;
} }
_bucketHeads[index] = head->NextLevel; _bucketHeads[index] = head->NextLevel;
node = head; node = head;
node->IsInBucket = false; node->IsInBucket = false;
node->NextLevel = null; node->NextLevel = null;
if (next != null) { if (next != null) {
next->PreviousLevel = null; next->PreviousLevel = null;
} }
return true; return true;
} }
private bool TryRemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node) private bool TryRemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
{ {
Index index = levelIndex > 9 ? ^1 : (3 * levelIndex) + sizeIndex; Index index = levelIndex > 9 ? ^1 : (3 * levelIndex) + sizeIndex;
if (_bucketHeads[index] == node) if (_bucketHeads[index] == node)
{ {
bool ok = TryPopLevel(levelIndex, sizeIndex, out _); bool ok = TryPopLevel(levelIndex, sizeIndex, out _);
return ok; return ok;
} }
BucketNode* previous = node->PreviousLevel; BucketNode* previous = node->PreviousLevel;
BucketNode* next = node->NextLevel; BucketNode* next = node->NextLevel;
if (previous != null) { if (previous != null) {
previous->NextLevel = next; previous->NextLevel = next;
} }
if (next != null) { if (next != null) {
next->PreviousLevel = previous; next->PreviousLevel = previous;
} }
node->NextLevel = null; node->NextLevel = null;
node->PreviousLevel = null; node->PreviousLevel = null;
node->IsInBucket = false; node->IsInBucket = false;
node->EntryRef = null; node->EntryRef = null;
return true; return true;
} }
/// <summary> /// <summary>
/// BucketNode structure - specialized node for bucket linked lists Stored in main /// BucketNode structure - specialized node for bucket linked lists Stored in main
/// allocation area (user-overwritable region) /// allocation area (user-overwritable region)
/// </summary> /// </summary>
[StructLayout(LayoutKind.Explicit)] [StructLayout(LayoutKind.Explicit)]
internal struct BucketNode internal struct BucketNode
{ {
[FieldOffset(0)] internal Entry* EntryRef; // Pointer to corresponding Entry [FieldOffset(0)] internal Entry* EntryRef; // Pointer to corresponding Entry
// [FieldOffset(16)] internal ulong LockBuffer; // [FieldOffset(16)] internal ulong LockBuffer;
[FieldOffset(8)] internal BucketNode* NextLevel; // Bucket stack linked list: next pointer [FieldOffset(8)] internal BucketNode* NextLevel; // Bucket stack linked list: next pointer
[FieldOffset(24)] internal BucketNode* PreviousLevel; // Bucket stack linked list: previous pointer [FieldOffset(24)] internal BucketNode* PreviousLevel; // Bucket stack linked list: previous pointer
[FieldOffset(32)] internal bool IsInBucket; // Whether in bucket [FieldOffset(32)] internal bool IsInBucket; // Whether in bucket
} }
} }
} }
@@ -1,122 +1,122 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="HLSFAllocator.Debug" // file="HLSFAllocator.Debug"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.Mathematics.Memory namespace StgSharp.Mathematics.Memory
{ {
public sealed class HLSFPositionChainBreakException : Exception public sealed class HLSFPositionChainBreakException : Exception
{ {
public HLSFPositionChainBreakException() : base("Position chain of an HLSF allocator is broken") { } public HLSFPositionChainBreakException() : base("Position chain of an HLSF allocator is broken") { }
} }
public unsafe partial class HybridLayerSegregatedFitAllocator public unsafe partial class HybridLayerSegregatedFitAllocator
{ {
private object _lock = new object(); private object _lock = new object();
public void DisplayEntryLayout(bool singleStep) public void DisplayEntryLayout(bool singleStep)
{ {
lock (_lock) lock (_lock)
{ {
Entry* cur = _spareMemory; Entry* cur = _spareMemory;
while (cur != null) while (cur != null)
{ {
Console.WriteLine(EntryToString(cur)); Console.WriteLine(EntryToString(cur));
cur = cur->PreviousNear; cur = cur->PreviousNear;
if (singleStep) { if (singleStep) {
Console.ReadKey(); Console.ReadKey();
} }
} }
} }
} }
public void DumpPositionChain(bool singleStep = false, int maxCount = 4096) public void DumpPositionChain(bool singleStep = false, int maxCount = 4096)
{ {
lock (_lock) lock (_lock)
{ {
try try
{ {
Entry* cur = _spareMemory; Entry* cur = _spareMemory;
int cnt = 0; int cnt = 0;
if (cur != null) { if (cur != null) {
cur = cur->NextNear; cur = cur->NextNear;
} }
Console.WriteLine(EntryToString(_spareMemory)); Console.WriteLine(EntryToString(_spareMemory));
while (cur != null && cur != _spareMemory && cnt < maxCount) while (cur != null && cur != _spareMemory && cnt < maxCount)
{ {
Console.WriteLine(EntryToString(cur)); Console.WriteLine(EntryToString(cur));
cnt++; cnt++;
if (singleStep) { if (singleStep) {
Console.ReadKey(); Console.ReadKey();
} }
cur = cur->NextNear; cur = cur->NextNear;
} }
if (cur == _spareMemory) if (cur == _spareMemory)
{ {
Console.WriteLine("(reached sentinel)"); Console.WriteLine("(reached sentinel)");
} else if (cnt >= maxCount) { } else if (cnt >= maxCount) {
Console.WriteLine("(stopped at maxCount)"); Console.WriteLine("(stopped at maxCount)");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[DumpPositionChain-forward] Exception: {ex.Message}"); Console.WriteLine($"[DumpPositionChain-forward] Exception: {ex.Message}");
} }
} }
} }
public IEnumerable<string> ShowEntryLayout() public IEnumerable<string> ShowEntryLayout()
{ {
lock (_lock) lock (_lock)
{ {
Stack<string> layout = []; Stack<string> layout = [];
Entry* cur = _spareMemory; Entry* cur = _spareMemory;
while (cur != null) while (cur != null)
{ {
layout.Push(EntryToString(cur)); layout.Push(EntryToString(cur));
cur = cur->PreviousNear; cur = cur->PreviousNear;
} }
return layout; return layout;
} }
} }
private static string EntryToString(Entry* e) private static string EntryToString(Entry* e)
{ {
BucketNode* b = (BucketNode*)e->Position; 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}"; 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" // file="HLSFAllocator.Free"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using System.Reflection.Emit; using System.Reflection.Emit;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using hlsfHandle = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocationHandle; using hlsfHandle = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocationHandle;
namespace StgSharp.Mathematics.Memory namespace StgSharp.Mathematics.Memory
{ {
public enum FreePolicy public enum FreePolicy
{ {
FullCollect, FullCollect,
LazyCollect, LazyCollect,
NoCollect NoCollect
} }
public unsafe partial class HybridLayerSegregatedFitAllocator public unsafe partial class HybridLayerSegregatedFitAllocator
{ {
/// <summary> /// <summary>
/// Minimal collect: linear scan, merge contiguous empty Blocks into one, push merged head /// Minimal collect: linear scan, merge contiguous empty Blocks into one, push merged head
/// back to bucket. Caller-responsible design: no slicing or redistribution. /// back to bucket. Caller-responsible design: no slicing or redistribution.
/// </summary> /// </summary>
public void Collect() public void Collect()
{ {
Entry* cur = _spareMemory->NextNear; Entry* cur = _spareMemory->NextNear;
long size = 0; long size = 0;
while (cur != null && cur != _spareMemory) while (cur != null && cur != _spareMemory)
{ {
// cur is not empty, skip // cur is not empty, skip
if (cur->State != EntryState.Empty) if (cur->State != EntryState.Empty)
{ {
cur = cur->NextNear; cur = cur->NextNear;
continue; continue;
} }
// remove cur from bucket // remove cur from bucket
do do
{ {
size += cur->Size; size += cur->Size;
Entry* next = cur->NextNear; Entry* next = cur->NextNear;
RemoveFromBucket(cur->Level, DetermineSegmentIndex(_levelSizeArray, cur->Size, cur->Level), (BucketNode*)cur->Position); RemoveFromBucket(cur->Level, DetermineSegmentIndex(_levelSizeArray, cur->Size, cur->Level), (BucketNode*)cur->Position);
RemoveFromPositionChain(cur); RemoveFromPositionChain(cur);
cur = next; cur = next;
} while (cur != null && cur != _spareMemory && cur->State == EntryState.Empty); } while (cur != null && cur != _spareMemory && cur->State == EntryState.Empty);
if (size > 0) { if (size > 0) {
SliceMemory(cur, size); SliceMemory(cur, size);
} }
} }
} }
/// <summary> /// <summary>
/// Free allocated memory block and perform adjacent block merging /// Free allocated memory block and perform adjacent block merging
/// </summary> /// </summary>
/// <param name="handle"> /// <param name="handle">
/// Handle to the allocated memory block /// Handle to the allocated memory block
/// <param name="collectPolicy"> /// <param name="collectPolicy">
/// </param> /// </param>
public void Free(hlsfHandle handle, FreePolicy collectPolicy = FreePolicy.NoCollect) public void Free(hlsfHandle handle, FreePolicy collectPolicy = FreePolicy.NoCollect)
{ {
Entry* e = handle.EntryHandle; Entry* e = handle.EntryHandle;
if (e == null) { if (e == null) {
return; return;
} }
if (e->State != EntryState.Alloc) { if (e->State != EntryState.Alloc) {
return; return;
} }
// Mark as empty so it can be merged // Mark as empty so it can be merged
e->State = EntryState.Empty; e->State = EntryState.Empty;
// Try merge with adjacent free blocks and remove them from buckets // Try merge with adjacent free blocks and remove them from buckets
if (collectPolicy == FreePolicy.LazyCollect) if (collectPolicy == FreePolicy.LazyCollect)
{ {
MergeAndRemoveFromBuckets(e); MergeAndRemoveFromBuckets(e);
} }
// Calculate final level and segment for the merged block // Calculate final level and segment for the merged block
long finalSize = e->Size; long finalSize = e->Size;
int finalLevel = GetLevelFromSize(finalSize); int finalLevel = GetLevelFromSize(finalSize);
e->Level = finalLevel; e->Level = finalLevel;
int finalSegment = DetermineSegmentIndex(_levelSizeArray, finalSize, finalLevel); int finalSegment = DetermineSegmentIndex(_levelSizeArray, finalSize, finalLevel);
// Push the merged entry back to appropriate bucket // Push the merged entry back to appropriate bucket
PushBucketToLevel(finalLevel, finalSegment, (BucketNode*)e->Position); PushBucketToLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
// Console.WriteLine("one block freed"); // Console.WriteLine("one block freed");
if (collectPolicy == FreePolicy.FullCollect) if (collectPolicy == FreePolicy.FullCollect)
{ {
Collect(); Collect();
} }
} }
} }
} }
@@ -1,92 +1,92 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="HLSFAllocator.Level" // file="HLSFAllocator.Level"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.Mathematics.Memory namespace StgSharp.Mathematics.Memory
{ {
public unsafe partial class HybridLayerSegregatedFitAllocator public unsafe partial class HybridLayerSegregatedFitAllocator
{ {
private const int MaxLevel = 9; private const int MaxLevel = 9;
private int[] _levelSizeArray = [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216]; private int[] _levelSizeArray = [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216];
public int AlignOfLevel(int bucketLevel) public int AlignOfLevel(int bucketLevel)
{ {
int blockSize = _levelSizeArray[bucketLevel]; int blockSize = _levelSizeArray[bucketLevel];
return int.Min(blockSize, _align); return int.Min(blockSize, _align);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetLevelFromSize(long size) private static int GetLevelFromSize(long size)
{ {
/* /*
* Level sizes: [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, ......] * 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) * Pattern: 64 * 4^level = 2^6 * 2^(2*level) = 2^(6+2*level)
* *
* For size S, we need: 2^(6+2*level) >= S * For size S, we need: 2^(6+2*level) >= S
* So: 6+2*level >= log2(S) * So: 6+2*level >= log2(S)
* Therefore: level >= (log2(S) - 6) / 2 * Therefore: level >= (log2(S) - 6) / 2
* *
* We use hardware LZCNT instruction via BitOperations.LeadingZeroCount * We use hardware LZCNT instruction via BitOperations.LeadingZeroCount
* log2(S) = 63 - LeadingZeroCount(S) for 64-bit values * log2(S) = 63 - LeadingZeroCount(S) for 64-bit values
*/ */
int log2Size = 63 - BitOperations.LeadingZeroCount((ulong)size); int log2Size = 63 - BitOperations.LeadingZeroCount((ulong)size);
// Calculate level: level = max(0, (log2Size - 6) / 2) // Calculate level: level = max(0, (log2Size - 6) / 2)
int level = Math.Max(0, (log2Size - 6) / 2); int level = Math.Max(0, (log2Size - 6) / 2);
// caller is responsible for ensuring level is within bounds // caller is responsible for ensuring level is within bounds
return level; return level;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetLevelFromSize(uint size) private static int GetLevelFromSize(uint size)
{ {
return GetLevelFromSize(size); return GetLevelFromSize(size);
} }
} }
public enum CommonMemoryAlign : int public enum CommonMemoryAlign : int
{ {
XMMLevel = 16, XMMLevel = 16,
YMMLevel = 32, YMMLevel = 32,
ZMMLevel = 64, ZMMLevel = 64,
ArmNEONLevel = 16, ArmNEONLevel = 16,
} }
} }
@@ -1,208 +1,208 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="HLSFAllocator.SegmentEntry" // file="HLSFAllocator.SegmentEntry"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace StgSharp.Mathematics.Memory namespace StgSharp.Mathematics.Memory
{ {
public unsafe partial class HybridLayerSegregatedFitAllocator public unsafe partial class HybridLayerSegregatedFitAllocator
{ {
/// <summary> /// <summary>
/// Check if two Entries are adjacent in memory /// Check if two Entries are adjacent in memory
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool AssertAdjacentTo(Entry* current, Entry* other) private static bool AssertAdjacentTo(Entry* current, Entry* other)
{ {
// Only a predicate; never throw here. Callers rely on false to skip merging. // Only a predicate; never throw here. Callers rely on false to skip merging.
return other->Position + (nuint)other->Size == current->Position || return other->Position + (nuint)other->Size == current->Position ||
current->Position + (nuint)current->Size == other->Position; current->Position + (nuint)current->Size == other->Position;
} }
/// <summary> /// <summary>
/// check out if a bucket is within the same higher-level boundary as currentOffset /// check out if a bucket is within the same higher-level boundary as currentOffset
/// </summary> /// </summary>
private bool AssertBoundary(Entry* previous, Entry* current, int level) private bool AssertBoundary(Entry* previous, Entry* current, int level)
{ {
level = int.Min(level, MaxLevel); level = int.Min(level, MaxLevel);
int nextLevelSize = _levelSizeArray[level] * 4; int nextLevelSize = _levelSizeArray[level] * 4;
ulong prevHead = previous->Position - (ulong)m_Buffer, ulong prevHead = previous->Position - (ulong)m_Buffer,
currHead = current->Position - (ulong)m_Buffer; currHead = current->Position - (ulong)m_Buffer;
return (currHead / (ulong)nextLevelSize) == (prevHead / (ulong)nextLevelSize); return (currHead / (ulong)nextLevelSize) == (prevHead / (ulong)nextLevelSize);
} }
/// <summary> /// <summary>
/// Determine segment index based on size and originLevel /// Determine segment index based on size and originLevel
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int DetermineSegmentIndex(int[] levelSizeArray, long size, int level) private static int DetermineSegmentIndex(int[] levelSizeArray, long size, int level)
{ {
level = int.Min(level, MaxLevel); level = int.Min(level, MaxLevel);
return (int)((size - 1) / levelSizeArray[level]); return (int)((size - 1) / levelSizeArray[level]);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void InsertEntryAfter(Entry* entry, Entry* position) private static void InsertEntryAfter(Entry* entry, Entry* position)
{ {
Entry* next = position->NextNear; Entry* next = position->NextNear;
entry->NextNear = next; entry->NextNear = next;
entry->PreviousNear = position; entry->PreviousNear = position;
next->PreviousNear = entry; next->PreviousNear = entry;
position->NextNear = entry; position->NextNear = entry;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void InsertEntryBefore(Entry* entry, Entry* position) private static void InsertEntryBefore(Entry* entry, Entry* position)
{ {
Entry* prev = position->PreviousNear; Entry* prev = position->PreviousNear;
entry->NextNear = position; entry->NextNear = position;
entry->PreviousNear = prev; entry->PreviousNear = prev;
prev->NextNear = entry; prev->NextNear = entry;
position->PreviousNear = entry; position->PreviousNear = entry;
} }
/// <summary> /// <summary>
/// Used in Free operations, efficiently remove from buckets and merge adjacent blocks. /// 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 /// Current Entry will serve as the final merged block. The Merge will be in lazy
/// strategy. /// strategy.
/// </summary> /// </summary>
private void MergeAndRemoveFromBuckets(Entry* entry) private void MergeAndRemoveFromBuckets(Entry* entry)
{ {
if (entry == null) { if (entry == null) {
return; return;
} }
long size = entry->Size; long size = entry->Size;
int originLevel = entry->Level; int originLevel = entry->Level;
int segment = DetermineSegmentIndex(_levelSizeArray, size, originLevel); int segment = DetermineSegmentIndex(_levelSizeArray, size, originLevel);
do do
{ {
// previous direction merge // previous direction merge
Entry* p = entry->PreviousNear; Entry* p = entry->PreviousNear;
if (p == null || if (p == null ||
p == _spareMemory || p == _spareMemory ||
p->State != EntryState.Empty || p->State != EntryState.Empty ||
p->Level != entry->Level || p->Level != entry->Level ||
!AssertBoundary(p, entry, originLevel)) !AssertBoundary(p, entry, originLevel))
{ {
break; break;
} }
// Console.WriteLine($"{(ulong)p->Size} merged to {(ulong)entry->Size} previous"); // Console.WriteLine($"{(ulong)p->Size} merged to {(ulong)entry->Size} previous");
entry->Size += p->Size; entry->Size += p->Size;
entry->Position = p->Position; entry->Position = p->Position;
RemoveFromBucket(originLevel, DetermineSegmentIndex(_levelSizeArray, p->Size, originLevel), (BucketNode*)p->Position); RemoveFromBucket(originLevel, DetermineSegmentIndex(_levelSizeArray, p->Size, originLevel), (BucketNode*)p->Position);
RemoveFromPositionChain(p); RemoveFromPositionChain(p);
} while (true); } while (true);
do do
{ {
// next direction merge // next direction merge
Entry* n = entry->NextNear; Entry* n = entry->NextNear;
if (n == null || if (n == null ||
n == _spareMemory || n == _spareMemory ||
n->State != EntryState.Empty || n->State != EntryState.Empty ||
n->Level != entry->Level || n->Level != entry->Level ||
!AssertBoundary(n, entry, originLevel)) !AssertBoundary(n, entry, originLevel))
{ {
break; break;
} }
// Console.WriteLine($"{(ulong)n->Size} merged to {(ulong)entry->Size} next"); // Console.WriteLine($"{(ulong)n->Size} merged to {(ulong)entry->Size} next");
entry->Size += n->Size; entry->Size += n->Size;
RemoveFromBucket(originLevel, DetermineSegmentIndex(_levelSizeArray, n->Size, originLevel), (BucketNode*)n->Position); RemoveFromBucket(originLevel, DetermineSegmentIndex(_levelSizeArray, n->Size, originLevel), (BucketNode*)n->Position);
RemoveFromPositionChain(n); RemoveFromPositionChain(n);
} while (true); } while (true);
originLevel = int.Min(9, originLevel); originLevel = int.Min(9, originLevel);
entry->Level += entry->Size >= _levelSizeArray[originLevel] * 4 ? 1 : 0; entry->Level += entry->Size >= _levelSizeArray[originLevel] * 4 ? 1 : 0;
entry->Level &= 15; entry->Level &= 15;
BucketNode* bucket = (BucketNode*)entry->Position; BucketNode* bucket = (BucketNode*)entry->Position;
bucket->EntryRef = entry; bucket->EntryRef = entry;
} }
/// <summary> /// <summary>
/// Remove node from position linked list, this method does not recycle a BucketNode from /// Remove node from position linked list, this method does not recycle a BucketNode from
/// size bucket /// size bucket
/// </summary> /// </summary>
private void RemoveFromPositionChain(Entry* entry) private void RemoveFromPositionChain(Entry* entry)
{ {
Entry* prev = entry->PreviousNear; Entry* prev = entry->PreviousNear;
Entry* next = entry->NextNear; Entry* next = entry->NextNear;
prev->NextNear = next; prev->NextNear = next;
next->PreviousNear = prev; next->PreviousNear = prev;
// Clear node pointers // Clear node pointers
entry->NextNear = null; entry->NextNear = null;
entry->PreviousNear = null; entry->PreviousNear = null;
// Console.WriteLine("entry recycled."); // Console.WriteLine("entry recycled.");
_entries.Free((nuint)entry); _entries.Free((nuint)entry);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void SetEntryLevel(Entry* entry, nuint position, long size) private static void SetEntryLevel(Entry* entry, nuint position, long size)
{ {
entry ->Position = position; entry ->Position = position;
entry->Size = size; entry->Size = size;
entry->Level = GetLevelFromSize(size); entry->Level = GetLevelFromSize(size);
} }
/// <summary> /// <summary>
/// Entry structure - streamlined version (only position and state information) Stored in /// Entry structure - streamlined version (only position and state information) Stored in
/// external SLAB /// external SLAB
/// </summary> /// </summary>
[StructLayout(LayoutKind.Explicit, Size = 40)] [StructLayout(LayoutKind.Explicit, Size = 40)]
internal struct Entry internal struct Entry
{ {
[FieldOffset(0)] internal Entry* NextNear; // Position linked list: next pointer [FieldOffset(0)] internal Entry* NextNear; // Position linked list: next pointer
[FieldOffset(8)] internal Entry* PreviousNear; // Position linked list: previous pointer [FieldOffset(8)] internal Entry* PreviousNear; // Position linked list: previous pointer
[FieldOffset(24)] internal int Level; // Level of the block in the allocator [FieldOffset(24)] internal int Level; // Level of the block in the allocator
[FieldOffset(16)] internal long Size; // Block size information [FieldOffset(16)] internal long Size; // Block size information
[FieldOffset(36)] public EntryState State; [FieldOffset(36)] public EntryState State;
[FieldOffset(28)] public nuint Position; // Memory block position [FieldOffset(28)] public nuint Position; // Memory block position
} }
internal enum EntryState : uint internal enum EntryState : uint
{ {
Spare, Spare,
Empty, Empty,
Alloc, Alloc,
Large Large
} }
} }
} }
@@ -1,178 +1,178 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="HLSFAllocator" // file="HLSFAllocator"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Buffers; using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
namespace StgSharp.Mathematics.Memory namespace StgSharp.Mathematics.Memory
{ {
public unsafe partial class HybridLayerSegregatedFitAllocator : IDisposable public unsafe partial class HybridLayerSegregatedFitAllocator : IDisposable
{ {
// Bucket system field - directly in main allocator class // Bucket system field - directly in main allocator class
private readonly BucketNode *[] _bucketHeads; private readonly BucketNode *[] _bucketHeads;
// _spareMemory is also the beginning position of allocator // _spareMemory is also the beginning position of allocator
private Entry *_spareMemory; private Entry *_spareMemory;
private readonly byte* m_Buffer; private readonly byte* m_Buffer;
private bool disposedValue; private bool disposedValue;
private readonly int _align; private readonly int _align;
private readonly long _maxSize; private readonly long _maxSize;
private readonly nuint _size; private readonly nuint _size;
private readonly SlabAllocator<Entry> _entries; private readonly SlabAllocator<Entry> _entries;
public HybridLayerSegregatedFitAllocator(nuint byteSize) public HybridLayerSegregatedFitAllocator(nuint byteSize)
{ {
if (byteSize < 64) { if (byteSize < 64) {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false); _entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
_size = byteSize; _size = byteSize;
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, 16); m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, 16);
_align = 16; _align = 16;
_bucketHeads = new BucketNode *[_levelSizeArray.Length * 3 + 1]; _bucketHeads = new BucketNode *[_levelSizeArray.Length * 3 + 1];
_spareMemory = (Entry *)_entries.Allocate(); _spareMemory = (Entry *)_entries.Allocate();
_spareMemory->State = EntryState.Spare; _spareMemory->State = EntryState.Spare;
_spareMemory->PreviousNear = _spareMemory; _spareMemory->PreviousNear = _spareMemory;
_spareMemory->NextNear = _spareMemory; _spareMemory->NextNear = _spareMemory;
_spareMemory->Position = (nuint)m_Buffer; _spareMemory->Position = (nuint)m_Buffer;
_spareMemory->Size = (long)byteSize; _spareMemory->Size = (long)byteSize;
_maxSize = 32U * 1024U * 1024U; _maxSize = 32U * 1024U * 1024U;
} }
public HybridLayerSegregatedFitAllocator(nuint byteSize, uint maxBlockSize) public HybridLayerSegregatedFitAllocator(nuint byteSize, uint maxBlockSize)
{ {
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false); _entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, 16); m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, 16);
_align = 16; _align = 16;
_size = byteSize; _size = byteSize;
_spareMemory = (Entry *)_entries.Allocate(); _spareMemory = (Entry *)_entries.Allocate();
_spareMemory->State = EntryState.Empty; _spareMemory->State = EntryState.Empty;
_spareMemory->PreviousNear = _spareMemory; _spareMemory->PreviousNear = _spareMemory;
_spareMemory->NextNear = _spareMemory; _spareMemory->NextNear = _spareMemory;
_spareMemory->Position = (nuint)m_Buffer; _spareMemory->Position = (nuint)m_Buffer;
_spareMemory->Size = (long)byteSize; _spareMemory->Size = (long)byteSize;
int levelCountOrigin = _levelSizeArray.Length; int levelCountOrigin = _levelSizeArray.Length;
int levelCount = GetLevelFromSize(maxBlockSize) + 1; int levelCount = GetLevelFromSize(maxBlockSize) + 1;
Array.Resize(ref _levelSizeArray, levelCount); Array.Resize(ref _levelSizeArray, levelCount);
_bucketHeads = new BucketNode *[(levelCount * 3) + 1]; _bucketHeads = new BucketNode *[(levelCount * 3) + 1];
int size = _levelSizeArray[^1]; int size = _levelSizeArray[^1];
for (int i = levelCountOrigin; i < levelCount; i++) for (int i = levelCountOrigin; i < levelCount; i++)
{ {
size *= 4; size *= 4;
_levelSizeArray[i] = size; _levelSizeArray[i] = size;
} }
_maxSize = maxBlockSize; _maxSize = maxBlockSize;
} }
public HybridLayerSegregatedFitAllocator(nuint byteSize, int align) public HybridLayerSegregatedFitAllocator(nuint byteSize, int align)
{ {
if (align < 16 || (align & (align - 1)) != 0) { if (align < 16 || (align & (align - 1)) != 0) {
throw new ArgumentException( throw new ArgumentException(
"Alignment must be a power of two and at least 16 bytes."); "Alignment must be a power of two and at least 16 bytes.");
} }
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false); _entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, (nuint)align); m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, (nuint)align);
_align = align; _align = align;
_size = byteSize; _size = byteSize;
_bucketHeads = new BucketNode *[_levelSizeArray.Length * 3 + 1]; _bucketHeads = new BucketNode *[_levelSizeArray.Length * 3 + 1];
_spareMemory = (Entry *)_entries.Allocate(); _spareMemory = (Entry *)_entries.Allocate();
_spareMemory->State = EntryState.Empty; _spareMemory->State = EntryState.Empty;
_spareMemory->PreviousNear = _spareMemory; _spareMemory->PreviousNear = _spareMemory;
_spareMemory->NextNear = _spareMemory; _spareMemory->NextNear = _spareMemory;
_spareMemory->Position = (nuint)m_Buffer; _spareMemory->Position = (nuint)m_Buffer;
_spareMemory->Size = (long)byteSize; _spareMemory->Size = (long)byteSize;
_maxSize = 32U * 1024U * 1024U; _maxSize = 32U * 1024U * 1024U;
} }
public HybridLayerSegregatedFitAllocator(nuint byteSize, int align, long maxBlockSize) public HybridLayerSegregatedFitAllocator(nuint byteSize, int align, long maxBlockSize)
{ {
if (align < 16 || (align & (align - 1)) != 0) { if (align < 16 || (align & (align - 1)) != 0) {
throw new ArgumentException( throw new ArgumentException(
"Alignment must be a power of two and at least 16 bytes."); "Alignment must be a power of two and at least 16 bytes.");
} }
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false); _entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
m_Buffer = (byte*)NativeMemory.AlignedAlloc(byteSize, (nuint)align); m_Buffer = (byte*)NativeMemory.AlignedAlloc(byteSize, (nuint)align);
_align = align; _align = align;
_size = byteSize; _size = byteSize;
_spareMemory = (Entry *)_entries.Allocate(); _spareMemory = (Entry *)_entries.Allocate();
_spareMemory->State = EntryState.Empty; _spareMemory->State = EntryState.Empty;
_spareMemory->PreviousNear = _spareMemory; _spareMemory->PreviousNear = _spareMemory;
_spareMemory->NextNear = _spareMemory; _spareMemory->NextNear = _spareMemory;
_spareMemory->Position = (nuint)m_Buffer; _spareMemory->Position = (nuint)m_Buffer;
_spareMemory->Size = (long)byteSize; _spareMemory->Size = (long)byteSize;
int levelCountOrigin = _levelSizeArray.Length; int levelCountOrigin = _levelSizeArray.Length;
int levelCount = GetLevelFromSize(maxBlockSize) + 1; int levelCount = GetLevelFromSize(maxBlockSize) + 1;
Array.Resize(ref _levelSizeArray, levelCount); Array.Resize(ref _levelSizeArray, levelCount);
_bucketHeads = new BucketNode *[levelCount * 3 + 1]; _bucketHeads = new BucketNode *[levelCount * 3 + 1];
int size = _levelSizeArray[^1]; int size = _levelSizeArray[^1];
for (int i = levelCountOrigin; i < levelCount; i++) for (int i = levelCountOrigin; i < levelCount; i++)
{ {
size *= 4; size *= 4;
_levelSizeArray[i] = size; _levelSizeArray[i] = size;
} }
_maxSize = maxBlockSize; _maxSize = maxBlockSize;
} }
public void Dispose() public void Dispose()
{ {
Dispose(disposing:true); Dispose(disposing:true);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (!disposedValue) if (!disposedValue)
{ {
if (disposing) { if (disposing) {
_entries.Dispose(); _entries.Dispose();
} }
NativeMemory.AlignedFree(m_Buffer); NativeMemory.AlignedFree(m_Buffer);
disposedValue = true; disposedValue = true;
} }
} }
~HybridLayerSegregatedFitAllocator() ~HybridLayerSegregatedFitAllocator()
{ {
Dispose(disposing:false); Dispose(disposing:false);
} }
} }
} }
@@ -1,137 +1,137 @@
# HLSF (Hybrid Layer Segregated Fit) Allocator # 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. 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 ## Key Points
- O(1) allocate/free with 10 levels: 64B x 16MB; each level split into 1x/2x/3x segments - 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 - 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. - 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) - Single-threaded implementation (external sync required for multi-threaded use)
## Quick Use ## Quick Use
```csharp ```csharp
using var hlsf = new HybridLayerSegregatedFitAllocator(64 * 1024 * 1024); using var hlsf = new HybridLayerSegregatedFitAllocator(64 * 1024 * 1024);
var h = hlsf.Alloc(1024); var h = hlsf.Alloc(1024);
Span<byte> s = h.BufferHandle; s[0] = 42; Span<byte> s = h.BufferHandle; s[0] = 42;
hlsf.Free(h); hlsf.Free(h);
``` ```
## Core APIs ## Core APIs
### Alloc(uint size) ### Alloc(uint size)
- Purpose: Allocate a block of at least `size` bytes. - 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. - 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>`. - Output: `HybridLayerSegregatedFitAllocationHandle` with pointer access and `Span<byte>`.
- Behavior: - Behavior:
- Classify into level/segment and pop a matching bucket; escalate segments and levels if empty. - 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. - 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. - 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. - 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) ### Free(HybridLayerSegregatedFitAllocationHandle handle)
- Purpose: Free a previously allocated block. - Purpose: Free a previously allocated block.
- Behavior: - Behavior:
- Mark the corresponding `Entry` as `Empty`. - Mark the corresponding `Entry` as `Empty`.
- Merge with adjacent `Empty` entries in both directions (boundary and level constraints). - Merge with adjacent `Empty` entries in both directions (boundary and level constraints).
- Remove merged neighbors from buckets, update position chain, and optionally promote level. - Remove merged neighbors from buckets, update position chain, and optionally promote level.
- Re-enqueue the final `Entry` into the appropriate bucket. - Re-enqueue the final `Entry` into the appropriate bucket.
### Collect() ### Collect()
- Purpose: Reclaim and rebalance free entries without touching allocated blocks. - Purpose: Reclaim and rebalance free entries without touching allocated blocks.
- Behavior: - Behavior:
- Consolidate fragmented regions where possible and improve bucket coverage. - Consolidate fragmented regions where possible and improve bucket coverage.
- Intended to be cheap; safe to call between benchmark phases. - Intended to be cheap; safe to call between benchmark phases.
### BenchmarkDotNet (HLSF vs Libc) ### BenchmarkDotNet (HLSF vs Libc)
Environment: .NET 8.0.23 (Release), x64 RyuJIT AVX2, Windows 11, AMD Ryzen 7 6800H. Environment: .NET 8.0.23 (Release), x64 RyuJIT AVX2, Windows 11, AMD Ryzen 7 6800H.
| Method | Mean | StdDev | Ratio | Allocated | | Method | Mean | StdDev | Ratio | Allocated |
|------- |-----:|------:|------:|----------:| |------- |-----:|------:|------:|----------:|
| Hlsf | 838.4 µs | 40.6 µs | 1.00 | 64 B | | Hlsf | 838.4 µs | 40.6 µs | 1.00 | 64 B |
| Libc | 849.8 µs | 100.2 µs | 1.01 | 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. 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`). > 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) ## 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. - 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 - Segments per level: 1x, 2x, 3x
- Buckets: index = 3 * level + segment (overflow bucket for level > 9) - Buckets: index = 3 * level + segment (overflow bucket for level > 9)
- Metadata: Entry (external SLAB, position chain), BucketNode (user region, bucket chain) - Metadata: Entry (external SLAB, position chain), BucketNode (user region, bucket chain)
- Position chain: circular doubly linked list anchored by spare sentinel - 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 - 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 - 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 - Free/Merge: bidirectional merge under boundary and level constraints; update buckets; optional level promotion
- Alignment: default 16B; internal blocks aligned by slicing/edge calculation - Alignment: default 16B; internal blocks aligned by slicing/edge calculation
- Complexity: average O(1) with bounded levels and constant-time bucket ops - Complexity: average O(1) with bounded levels and constant-time bucket ops
- `Entry` (external SLAB): linkage in position chain, `Position`, `Size`, `Level`, `State` - `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 - 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` - `BucketNode` (in user region): `EntryRef`, `NextLevel`, `PreviousLevel`, `IsInBucket`
- Position chain: circular doubly linked list anchored by sentinel `_spareMemory` (`State.Spare`) - Position chain: circular doubly linked list anchored by sentinel `_spareMemory` (`State.Spare`)
### Allocation Flow ### Allocation Flow
1) Compute `level` and `segment` from requested `size` 1) Compute `level` and `segment` from requested `size`
2) Try pop bucket (`TryPopLevelForAllocation(level, segment, out entry)`) and escalate: 2) Try pop bucket (`TryPopLevelForAllocation(level, segment, out entry)`) and escalate:
- Advance `segment` 0��1��2, then `level++`, until `MaxLevel` - Advance `segment` 0��1��2, then `level++`, until `MaxLevel`
- If still empty: try pop from overflow bucket (L=10, seg=1) or `TryAllocateNew32MBBlock` - 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` 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 4) If remainder exists in the source region, call `SliceMemory(entry->NextNear, remainder)` to produce free chunks into buckets
### Slicing Logic (`SliceMemory`) ### Slicing Logic (`SliceMemory`)
- Inputs: `next` entry position, `sizeToSlice` - Inputs: `next` entry position, `sizeToSlice`
- Compute `offset` and `end` relative to base buffer; pick starting level by trailing-zero count of `offset`: - 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` - `i = (BitOperations.TrailingZeroCount(offset) - 6) / 2`, clamped to `�� MaxLevel`
- Three phases to cover [offset, end): - 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 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 2) If `i > MaxLevel`: pack in largest upper window on tail
3) Backward i-1..0: fill remaining tail with smaller sizes 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)`) - 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`) ### Free and Merge (`MergeAndRemoveFromBuckets`)
- Given an `Entry` to free (turned `Empty` by caller): - Given an `Entry` to free (turned `Empty` by caller):
- Merge left while neighbor `p` is `Empty`, same `Level`, and within boundary (`AssertBoundary`) - Merge left while neighbor `p` is `Empty`, same `Level`, and within boundary (`AssertBoundary`)
- Remove `p` from bucket (`RemoveFromBucket`), expand current, fix `Position`, unlink `p` - Remove `p` from bucket (`RemoveFromBucket`), expand current, fix `Position`, unlink `p`
- Merge right with neighbor `n` by the same rule - Merge right with neighbor `n` by the same rule
- Optionally promote `Level` if `Size �� levelSizeArray[originLevel] * 4` - Optionally promote `Level` if `Size �� levelSizeArray[originLevel] * 4`
- Ensure `BucketNode.EntryRef` points to the merged entry; caller re-enqueues into the proper bucket - Ensure `BucketNode.EntryRef` points to the merged entry; caller re-enqueues into the proper bucket
### Buckets ### Buckets
- `PushBucketToLevel(level, segment, node)`: push to bucket head, set `IsInBucket`, maintain prev/next - `PushBucketToLevel(level, segment, node)`: push to bucket head, set `IsInBucket`, maintain prev/next
- `TryPopLevel(level, segment, out node)`: pop head; if empty, return false - `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 - `RemoveFromBucket(level, segment, node)`: unlink from middle or use `TryPopLevel` if it is the head
### Large Allocations and Spare Memory ### Large Allocations and Spare Memory
- Requests exceeding the configured `maxBlockSize` are rejected with `InvalidOperationException`. All allocations are served from the pre-allocated arena. - 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 - 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 ### Alignment
- Default `_align = 16`; `AlignOfLevel(level)` returns `min(levelSizeArray[level], _align)` - Default `_align = 16`; `AlignOfLevel(level)` returns `min(levelSizeArray[level], _align)`
- Internal blocks align to level boundaries by design of slicing/edge calculations - Internal blocks align to level boundaries by design of slicing/edge calculations
### Complexity and Invariants ### Complexity and Invariants
- Allocate/free are O(1) on average with bounded levels (��10) and constant-time bucket ops - 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 - Invariants guarded by exceptions (e.g., `HLSFPositionChainBreakException`) and pointer consistency checks
## Profiling (hotspots, current workloads) ## Profiling (hotspots, current workloads)
- SliceMemory ~22% (self ~17%) �� slicing and bucket enqueue dominate allocation path - SliceMemory ~22% (self ~17%) �� slicing and bucket enqueue dominate allocation path
- MergeAndRemoveFromBuckets ~13% (self ~12%) �� merge + removal dominate free path - MergeAndRemoveFromBuckets ~13% (self ~12%) �� merge + removal dominate free path
- Collect ~7.6% (self ~7.5%) �� low overall cost - 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. 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" // file="SlabAllocationHandle"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace StgSharp.Mathematics.Memory namespace StgSharp.Mathematics.Memory
{ {
public unsafe struct SlabAllocationHandle<T> : IDisposable where T: unmanaged public unsafe struct SlabAllocationHandle<T> : IDisposable where T: unmanaged
{ {
private readonly SlabAllocator<T> _allocator; private readonly SlabAllocator<T> _allocator;
internal SlabAllocationHandle(SlabAllocator<T> allocator, bool locked) internal SlabAllocationHandle(SlabAllocator<T> allocator, bool locked)
{ {
_allocator = allocator; _allocator = allocator;
Locked = locked; Locked = locked;
} }
public bool Locked { get; set; } public bool Locked { get; set; }
public void Dispose() public void Dispose()
{ {
if (Locked) if (Locked)
{ {
_allocator.ExitBufferReading(); _allocator.ExitBufferReading();
Locked = false; Locked = false;
} }
} }
} }
} }
@@ -1,134 +1,134 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="SlabAllocator.Chunked" // file="SlabAllocator.Chunked"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using StgSharp.Threading; using StgSharp.Threading;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
namespace StgSharp.Mathematics.Memory namespace StgSharp.Mathematics.Memory
{ {
internal sealed unsafe class ChunkedSlabAllocator<T> : SlabAllocator<T> where T: unmanaged internal sealed unsafe class ChunkedSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
{ {
private bool _disposed; private bool _disposed;
private BufferStack<nuint> _buffers; private BufferStack<nuint> _buffers;
private BufferStack<nuint> _recycle; private BufferStack<nuint> _recycle;
private readonly int _elementSize = Unsafe.SizeOf<T>(); private readonly int _elementSize = Unsafe.SizeOf<T>();
private nuint _currentBuffer; private nuint _currentBuffer;
private nuint _currentCapacity, _currentIndex; private nuint _currentCapacity, _currentIndex;
public ChunkedSlabAllocator(nuint count) public ChunkedSlabAllocator(nuint count)
{ {
_currentBuffer = (nuint)NativeMemory.Alloc(count * (nuint)_elementSize); _currentBuffer = (nuint)NativeMemory.Alloc(count * (nuint)_elementSize);
_currentCapacity = count; _currentCapacity = count;
int initCount = count switch int initCount = count switch
{ {
<= 4 => 4, <= 4 => 4,
> 64 => 64, > 64 => 64,
_ => (int)count _ => (int)count
}; };
Span<nuint> span = stackalloc nuint[initCount]; Span<nuint> span = stackalloc nuint[initCount];
for (int i = 0; i < initCount; i++) { for (int i = 0; i < initCount; i++) {
span[i] = _currentBuffer + (nuint)(i * _elementSize); span[i] = _currentBuffer + (nuint)(i * _elementSize);
} }
_buffers = new(4); _buffers = new(4);
_buffers.Push(_currentBuffer); _buffers.Push(_currentBuffer);
_recycle = BufferStackBuilder.Create(span); _recycle = BufferStackBuilder.Create(span);
_currentCapacity = count; _currentCapacity = count;
_currentIndex = (nuint)initCount; _currentIndex = (nuint)initCount;
} }
public override nuint Allocate() public override nuint Allocate()
{ {
if (_recycle.TryPop(out nuint handle)) { if (_recycle.TryPop(out nuint handle)) {
return handle; return handle;
} }
// nuint bufferHandle = _currentBuffer; // nuint bufferHandle = _currentBuffer;
nuint curIndex = _currentIndex; nuint curIndex = _currentIndex;
if (curIndex >= _currentCapacity) if (curIndex >= _currentCapacity)
{ {
nuint cap = _currentCapacity; nuint cap = _currentCapacity;
nuint newBuffer = (nuint)NativeMemory.Alloc(cap * (nuint)_elementSize); nuint newBuffer = (nuint)NativeMemory.Alloc(cap * (nuint)_elementSize);
_buffers.Push(newBuffer); _buffers.Push(newBuffer);
_currentBuffer = newBuffer; _currentBuffer = newBuffer;
_currentIndex = 0; _currentIndex = 0;
} }
curIndex = _currentIndex; curIndex = _currentIndex;
_currentIndex += 1; _currentIndex += 1;
return _currentBuffer + ((nuint)_elementSize * curIndex); return _currentBuffer + ((nuint)_elementSize * curIndex);
} }
public override void Dispose() public override void Dispose()
{ {
if (_disposed) { if (_disposed) {
return; return;
} }
_disposed = true; _disposed = true;
// NativeMemory.Free((void*)_currentBuffer); // NativeMemory.Free((void*)_currentBuffer);
while (_buffers.TryPop(out nuint buffer)) while (_buffers.TryPop(out nuint buffer))
{ {
NativeMemory.Free((void*)buffer); NativeMemory.Free((void*)buffer);
} }
_buffers.Dispose(); _buffers.Dispose();
_recycle.Dispose(); _recycle.Dispose();
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void EnterBufferReading() { } public override void EnterBufferReading() { }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void ExitBufferReading() { } public override void ExitBufferReading() { }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Free(nuint index) public override void Free(nuint index)
{ {
_recycle.Push(index); _recycle.Push(index);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override SlabAllocationHandle<T> ReadAllocation() public override SlabAllocationHandle<T> ReadAllocation()
{ {
return new SlabAllocationHandle<T>(this, false); return new SlabAllocationHandle<T>(this, false);
} }
~ChunkedSlabAllocator() ~ChunkedSlabAllocator()
{ {
Dispose(); Dispose();
} }
} }
} }
@@ -1,157 +1,157 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="SlabAllocator.ChunkedConcurrent" // file="SlabAllocator.ChunkedConcurrent"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using StgSharp.Threading; using StgSharp.Threading;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.Mathematics.Memory namespace StgSharp.Mathematics.Memory
{ {
internal sealed unsafe class ConcurrentChunkedSlabAllocator<T> : SlabAllocator<T> where T: unmanaged internal sealed unsafe class ConcurrentChunkedSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
{ {
private bool _disposed; private bool _disposed;
private readonly BufferExpansionLock _lock = new(); private readonly BufferExpansionLock _lock = new();
private ConcurrentBufferStack<nuint> _buffers; private ConcurrentBufferStack<nuint> _buffers;
private ConcurrentBufferStack<nuint> _recycle; private ConcurrentBufferStack<nuint> _recycle;
private readonly int _elementSize = Unsafe.SizeOf<T>(); private readonly int _elementSize = Unsafe.SizeOf<T>();
private nuint _currentBuffer; private nuint _currentBuffer;
private nuint _currentCapacity, _currentIndex; private nuint _currentCapacity, _currentIndex;
public ConcurrentChunkedSlabAllocator(nuint count) public ConcurrentChunkedSlabAllocator(nuint count)
{ {
_currentBuffer = (nuint)NativeMemory.Alloc(count * (nuint)_elementSize); _currentBuffer = (nuint)NativeMemory.Alloc(count * (nuint)_elementSize);
_currentCapacity = count; _currentCapacity = count;
int initCount = count switch int initCount = count switch
{ {
<= 4 => 4, <= 4 => 4,
> 64 => 64, > 64 => 64,
_ => (int)count _ => (int)count
}; };
Span<nuint> span = stackalloc nuint[initCount]; Span<nuint> span = stackalloc nuint[initCount];
for (int i = 0; i < initCount; i++) { for (int i = 0; i < initCount; i++) {
span[i] = _currentBuffer + (nuint)(i * _elementSize); span[i] = _currentBuffer + (nuint)(i * _elementSize);
} }
_buffers = new(4); _buffers = new(4);
_recycle = BufferStackBuilder.CreateConcurrent(span); _recycle = BufferStackBuilder.CreateConcurrent(span);
_currentCapacity = count; _currentCapacity = count;
_currentIndex = (nuint)initCount; _currentIndex = (nuint)initCount;
} }
public override nuint Allocate() public override nuint Allocate()
{ {
if (_recycle.TryPop(out nuint handle)) { if (_recycle.TryPop(out nuint handle)) {
return handle; return handle;
} }
while (true) while (true)
{ {
nuint bufferHandle = Volatile.Read(ref _currentBuffer); nuint bufferHandle = Volatile.Read(ref _currentBuffer);
nuint curIndex = Volatile.Read(ref _currentIndex); nuint curIndex = Volatile.Read(ref _currentIndex);
_lock.EnterMetaDataRead(); _lock.EnterMetaDataRead();
try try
{ {
if (curIndex >= _currentCapacity) if (curIndex >= _currentCapacity)
{ {
_lock.ExitMetaDataRead(); _lock.ExitMetaDataRead();
_lock.EnterExpansionProcess(); _lock.EnterExpansionProcess();
try try
{ {
nuint cap = Volatile.Read(ref _currentCapacity); nuint cap = Volatile.Read(ref _currentCapacity);
nuint newBuffer = (nuint)NativeMemory.Alloc(cap * (nuint)_elementSize); nuint newBuffer = (nuint)NativeMemory.Alloc(cap * (nuint)_elementSize);
_buffers.Push(bufferHandle); _buffers.Push(bufferHandle);
_ = Interlocked.Exchange(ref _currentBuffer, newBuffer); _ = Interlocked.Exchange(ref _currentBuffer, newBuffer);
_ = Interlocked.Exchange(ref _currentIndex, 0); _ = Interlocked.Exchange(ref _currentIndex, 0);
} }
finally finally
{ {
_lock.ExitExpansionProcess(); _lock.ExitExpansionProcess();
} }
} }
curIndex = Volatile.Read(ref _currentIndex); curIndex = Volatile.Read(ref _currentIndex);
_ = Interlocked.Add( _ = Interlocked.Add(
ref Unsafe.As<nuint, ulong>(ref _currentIndex), (ulong)_elementSize); ref Unsafe.As<nuint, ulong>(ref _currentIndex), (ulong)_elementSize);
return _currentBuffer + ((nuint)_elementSize * curIndex); return _currentBuffer + ((nuint)_elementSize * curIndex);
} }
finally finally
{ {
if (_lock.IsThreadReadingMetaData) { if (_lock.IsThreadReadingMetaData) {
_lock.ExitMetaDataRead(); _lock.ExitMetaDataRead();
} }
} }
} }
} }
public override void Dispose() public override void Dispose()
{ {
if (_disposed) { if (_disposed) {
return; return;
} }
_disposed = true; _disposed = true;
NativeMemory.Free((void*)_currentBuffer); NativeMemory.Free((void*)_currentBuffer);
while (_buffers.TryPop(out nuint buffer)) { while (_buffers.TryPop(out nuint buffer)) {
NativeMemory.Free((void*)buffer); NativeMemory.Free((void*)buffer);
} }
_lock.Dispose(); _lock.Dispose();
_buffers.Dispose(); _buffers.Dispose();
_recycle.Dispose(); _recycle.Dispose();
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void EnterBufferReading() { } public override void EnterBufferReading() { }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void ExitBufferReading() { } public override void ExitBufferReading() { }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Free(nuint index) public override void Free(nuint index)
{ {
_recycle.Push(index); _recycle.Push(index);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override SlabAllocationHandle<T> ReadAllocation() public override SlabAllocationHandle<T> ReadAllocation()
{ {
return new SlabAllocationHandle<T>(this, false); return new SlabAllocationHandle<T>(this, false);
} }
~ConcurrentChunkedSlabAllocator() ~ConcurrentChunkedSlabAllocator()
{ {
Dispose(); Dispose();
} }
} }
} }
@@ -1,165 +1,165 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="SlabAllocator.SequencialConcurrent" // file="SlabAllocator.SequencialConcurrent"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using StgSharp.Threading; using StgSharp.Threading;
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
namespace StgSharp.Mathematics.Memory namespace StgSharp.Mathematics.Memory
{ {
public sealed unsafe class ConcurrentSequentialSlabAllocator<T> : SlabAllocator<T> where T: unmanaged public sealed unsafe class ConcurrentSequentialSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
{ {
private T* _buffer; private T* _buffer;
private bool _disposed; private bool _disposed;
private readonly BufferExpansionLock _lock = new(); private readonly BufferExpansionLock _lock = new();
private readonly ConcurrentBufferStack<nuint> _stack; private readonly ConcurrentBufferStack<nuint> _stack;
private readonly int _elementSize; private readonly int _elementSize;
private nuint _highestAllocated; private nuint _highestAllocated;
private ulong _capacity; private ulong _capacity;
public ConcurrentSequentialSlabAllocator(nuint initialCapacity) public ConcurrentSequentialSlabAllocator(nuint initialCapacity)
{ {
_elementSize = Unsafe.SizeOf<T>(); _elementSize = Unsafe.SizeOf<T>();
_capacity = initialCapacity; _capacity = initialCapacity;
_highestAllocated = 0; _highestAllocated = 0;
_buffer = (T*)NativeMemory.Alloc((nuint)_capacity * (nuint)_elementSize); _buffer = (T*)NativeMemory.Alloc((nuint)_capacity * (nuint)_elementSize);
int initCount = _capacity switch int initCount = _capacity switch
{ {
<= 4 => 4, <= 4 => 4,
> 64 => 64, > 64 => 64,
_ => (int)_capacity / 4 _ => (int)_capacity / 4
}; };
Span<nuint> span = stackalloc nuint[initCount]; Span<nuint> span = stackalloc nuint[initCount];
for (int i = 0; i < initCount; i++) { for (int i = 0; i < initCount; i++) {
span[i] = (nuint)i; span[i] = (nuint)i;
} }
_stack = BufferStackBuilder.CreateConcurrent(span); _stack = BufferStackBuilder.CreateConcurrent(span);
_highestAllocated += (nuint)initCount; _highestAllocated += (nuint)initCount;
} }
internal T* BasePointer internal T* BasePointer
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _buffer; get => _buffer;
} }
public override nuint Allocate() public override nuint Allocate()
{ {
if (_stack.TryPop(out nuint index)) { if (_stack.TryPop(out nuint index)) {
return index; return index;
} }
while (true) while (true)
{ {
index = Volatile.Read(ref _highestAllocated); index = Volatile.Read(ref _highestAllocated);
_lock.EnterMetaDataRead(); _lock.EnterMetaDataRead();
try try
{ {
if (index >= _capacity) if (index >= _capacity)
{ {
_lock.ExitMetaDataRead(); _lock.ExitMetaDataRead();
_lock.EnterExpansionProcess(); _lock.EnterExpansionProcess();
try try
{ {
_capacity = Volatile.Read(ref _capacity) * 2; _capacity = Volatile.Read(ref _capacity) * 2;
nuint newSize = (nuint)_capacity * (nuint)_elementSize; nuint newSize = (nuint)_capacity * (nuint)_elementSize;
_lock.EnterBufferCopy(); _lock.EnterBufferCopy();
try try
{ {
_buffer = (T*)NativeMemory.Realloc(_buffer, newSize); _buffer = (T*)NativeMemory.Realloc(_buffer, newSize);
} }
finally finally
{ {
_lock.ExitBufferCopy(); _lock.ExitBufferCopy();
} }
} }
finally finally
{ {
_lock.ExitExpansionProcess(); _lock.ExitExpansionProcess();
} }
} }
_ = Interlocked.Increment(ref Unsafe.As<nuint, ulong>(ref _highestAllocated)); _ = Interlocked.Increment(ref Unsafe.As<nuint, ulong>(ref _highestAllocated));
return index; return index;
} }
finally finally
{ {
if (_lock.IsThreadReadingMetaData) { if (_lock.IsThreadReadingMetaData) {
_lock.ExitMetaDataRead(); _lock.ExitMetaDataRead();
} }
} }
} }
} }
public override void Dispose() public override void Dispose()
{ {
if (_disposed) { if (_disposed) {
return; return;
} }
_disposed = true; _disposed = true;
_stack.Dispose(); _stack.Dispose();
NativeMemory.Free(_buffer); NativeMemory.Free(_buffer);
_lock.Dispose(); _lock.Dispose();
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void EnterBufferReading() public override void EnterBufferReading()
{ {
_lock.EnterBufferRead(); _lock.EnterBufferRead();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void ExitBufferReading() public override void ExitBufferReading()
{ {
_lock.ExitBufferRead(); _lock.ExitBufferRead();
} }
public override void Free(nuint index) public override void Free(nuint index)
{ {
_stack.Push(index); _stack.Push(index);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override SlabAllocationHandle<T> ReadAllocation() public override SlabAllocationHandle<T> ReadAllocation()
{ {
EnterBufferReading(); EnterBufferReading();
return new SlabAllocationHandle<T>(this, true); return new SlabAllocationHandle<T>(this, true);
} }
~ConcurrentSequentialSlabAllocator() ~ConcurrentSequentialSlabAllocator()
{ {
Dispose(); Dispose();
} }
} }
} }
@@ -1,118 +1,118 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="SlabAllocator" // file="SlabAllocator"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
namespace StgSharp.Mathematics.Memory namespace StgSharp.Mathematics.Memory
{ {
public abstract class SlabAllocator<T> : IDisposable where T: unmanaged public abstract class SlabAllocator<T> : IDisposable where T: unmanaged
{ {
public abstract nuint Allocate(); public abstract nuint Allocate();
/// <summary> /// <summary>
/// Creates a new SLAB allocator instance with the specified configuration. /// Creates a new SLAB allocator instance with the specified configuration.
/// </summary> /// </summary>
/// <param name="count"> /// <param name="count">
/// Initial number of objects that can be allocated. /// Initial number of objects that can be allocated.
/// </param> /// </param>
/// <param name="layout"> /// <param name="layout">
/// Memory layout strategy for the SLAB allocator. /// Memory layout strategy for the SLAB allocator.
/// </param> /// </param>
/// <param name="concurrentSupport"> /// <param name="concurrentSupport">
/// Whether to enable concurrent access support. /// Whether to enable concurrent access support.
/// </param> /// </param>
/// <returns> /// <returns>
/// A new SLAB allocator instance. /// A new SLAB allocator instance.
/// </returns> /// </returns>
/// <exception cref="ArgumentOutOfRangeException"> /// <exception cref="ArgumentOutOfRangeException">
/// Thrown when an invalid layout is specified. /// Thrown when an invalid layout is specified.
/// </exception> /// </exception>
/// <exception cref="NotSupportedException"> /// <exception cref="NotSupportedException">
/// Thrown when requesting unsupported configurations. /// Thrown when requesting unsupported configurations.
/// </exception> /// </exception>
/// <remarks> /// <remarks>
/// <para><strong> Important Threading Considerations: </strong></para> <para>While SLAB /// <para><strong> Important Threading Considerations: </strong></para> <para>While SLAB
/// /// allocators themselves may be designed for single-threaded use, allocated objects /// /// allocators themselves may be designed for single-threaded use, allocated objects
/// are /// frequently shared across multiple threads in real-world applications. Due to /// are /// frequently shared across multiple threads in real-world applications. Due to
/// the /// special implementation of expandable concurrent sequential SLAB allocators, /// the /// special implementation of expandable concurrent sequential SLAB allocators,
/// they rely /// on internal expansion locks to ensure that memory buffer expansions /// 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 /// (which involve /// copying the entire buffer to a new location) do not occur while
/// other threads are /// reading from allocated regions. </para> <para><strong> /// other threads are /// reading from allocated regions. </para> <para><strong>
/// Sequential Layout Limitations: ///</strong><br /> Sequential layout SLAB allocators /// Sequential Layout Limitations: ///</strong><br /> Sequential layout SLAB allocators
/// cannot provide a single-threaded /// version because:<list /// cannot provide a single-threaded /// version because:<list
/// type="bullet"><item><description>They use contiguous memory /// layout that requires /// type="bullet"><item><description>They use contiguous memory /// layout that requires
/// buffer expansion when capacity is exceeded /// buffer expansion when capacity is exceeded
/// ///</description></item><item><description>Buffer expansion involves copying all /// /// ///</description></item><item><description>Buffer expansion involves copying all ///
/// existing data to a new memory location</description></item><item><description> /// /// existing data to a new memory location</description></item><item><description> ///
/// Without proper synchronization, concurrent access to allocated objects during /// /// Without proper synchronization, concurrent access to allocated objects during ///
/// expansion would cause memory corruption</description></item><item><description>The /// expansion would cause memory corruption</description></item><item><description>The
/// /// expansion lock mechanism is essential even if allocation itself is single- /// /// /// expansion lock mechanism is essential even if allocation itself is single- ///
/// threaded </description></item></list></para> <para><strong>Chunked Layout: /// threaded </description></item></list></para> <para><strong>Chunked Layout:
/// ///</strong><br /> Chunked layout allocators can optionally provide single-threaded /// ///</strong><br /> Chunked layout allocators can optionally provide single-threaded
/// /// versions since they don't require buffer expansion and copying. Each chunk is /// /// versions since they don't require buffer expansion and copying. Each chunk is
/// /// allocated independently, avoiding the memory relocation issues present in /// /// allocated independently, avoiding the memory relocation issues present in
/// sequential /// layouts. </para> /// sequential /// layouts. </para>
/// </remarks> /// </remarks>
public static SlabAllocator<T> Create(nuint count, SlabBufferLayout layout, bool concurrentSupport = true) public static SlabAllocator<T> Create(nuint count, SlabBufferLayout layout, bool concurrentSupport = true)
{ {
if (concurrentSupport) { if (concurrentSupport) {
return layout switch return layout switch
{ {
SlabBufferLayout.Sequential => new ConcurrentSequentialSlabAllocator<T>(count), SlabBufferLayout.Sequential => new ConcurrentSequentialSlabAllocator<T>(count),
SlabBufferLayout.Chunked => new ConcurrentChunkedSlabAllocator<T>(count), SlabBufferLayout.Chunked => new ConcurrentChunkedSlabAllocator<T>(count),
_ => throw new ArgumentOutOfRangeException(nameof(layout), layout, null) _ => throw new ArgumentOutOfRangeException(nameof(layout), layout, null)
}; };
} }
return layout switch 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.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), SlabBufferLayout.Chunked => new ChunkedSlabAllocator<T>(count),
_ => throw new ArgumentOutOfRangeException(nameof(layout), layout, null) _ => throw new ArgumentOutOfRangeException(nameof(layout), layout, null)
}; };
} }
public abstract void Dispose(); public abstract void Dispose();
public abstract void EnterBufferReading(); public abstract void EnterBufferReading();
public abstract void ExitBufferReading(); public abstract void ExitBufferReading();
public abstract void Free(nuint index); public abstract void Free(nuint index);
public abstract SlabAllocationHandle<T> ReadAllocation(); public abstract SlabAllocationHandle<T> ReadAllocation();
} }
public enum SlabBufferLayout public enum SlabBufferLayout
{ {
Sequential, Sequential,
Chunked Chunked
} }
} }
@@ -1,98 +1,98 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="IRegisterType" // file="IRegisterType"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.Mathematics namespace StgSharp.Mathematics
{ {
/// <summary> /// <summary>
/// Abstraction for a wide SIMD register or equivalent packed numeric storage. Provides raw /// Abstraction for a wide SIMD register or equivalent packed numeric storage. Provides raw
/// reference access, broadcast initialization and element level access. Typical /// reference access, broadcast initialization and element level access. Typical
/// implementations map to 128/256/512-bit hardware registers or software emulated blocks. /// implementations map to 128/256/512-bit hardware registers or software emulated blocks.
/// </summary> /// </summary>
public interface IRegisterPresentation public interface IRegisterPresentation
{ {
/// <summary> /// <summary>
/// Gets a reference to the first element of the register reinterpreted as type <typeparamref /// Gets a reference to the first element of the register reinterpreted as type <typeparamref
/// name="T" />. /// name="T" />.
/// </summary> /// </summary>
/// <typeparam name="T"> /// <typeparam name="T">
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />. /// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
/// </typeparam> /// </typeparam>
/// <returns> /// <returns>
/// Writable reference to element 0 (lowest lane / lowest bits). /// Writable reference to element 0 (lowest lane / lowest bits).
/// </returns> /// </returns>
/// <remarks> /// <remarks>
/// Intended for treating the register as a contiguous block of elements. The returned reference /// 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 /// 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 /// cref="Member{T}(int)" /> with index 0. Caller must choose a type layout compatible with the
/// actual register representation. /// actual register representation.
/// </remarks> /// </remarks>
ref T AsRef<T>() where T: unmanaged, INumber<T>; ref T AsRef<T>() where T: unmanaged, INumber<T>;
/// <summary> /// <summary>
/// Fills all elements (lanes) of the register with the provided scalar <paramref name="value" />. /// Fills all elements (lanes) of the register with the provided scalar <paramref name="value" />.
/// </summary> /// </summary>
/// <typeparam name="T"> /// <typeparam name="T">
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />. /// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
/// </typeparam> /// </typeparam>
/// <param name="value"> /// <param name="value">
/// Scalar value to replicate to every element. /// Scalar value to replicate to every element.
/// </param> /// </param>
/// <remarks> /// <remarks>
/// Should map to a single broadcast/shuffle instruction where possible. After completion all /// Should map to a single broadcast/shuffle instruction where possible. After completion all
/// lanes read back the same value. /// lanes read back the same value.
/// </remarks> /// </remarks>
void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>; void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>;
/// <summary> /// <summary>
/// Gets a writable reference to the element at the specified <paramref name="index" />. /// Gets a writable reference to the element at the specified <paramref name="index" />.
/// </summary> /// </summary>
/// <typeparam name="T"> /// <typeparam name="T">
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />. /// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
/// </typeparam> /// </typeparam>
/// <param name="index"> /// <param name="index">
/// Zero-based lane index (range defined by implementation). /// Zero-based lane index (range defined by implementation).
/// </param> /// </param>
/// <returns> /// <returns>
/// Writable reference to the lane. /// Writable reference to the lane.
/// </returns> /// </returns>
/// <remarks> /// <remarks>
/// Enables scalar lane edits without unpacking the entire register. Implementations may omit /// Enables scalar lane edits without unpacking the entire register. Implementations may omit
/// bounds checks for performance. Caller is responsible for passing a valid index. /// bounds checks for performance. Caller is responsible for passing a valid index.
/// </remarks> /// </remarks>
ref T Member<T>(int index) where T: unmanaged, INumber<T>; ref T Member<T>(int index) where T: unmanaged, INumber<T>;
} }
} }
@@ -1,120 +1,120 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="M128" // file="M128"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
using System.Text; using System.Text;
namespace StgSharp.Mathematics namespace StgSharp.Mathematics
{ {
[StructLayout(LayoutKind.Explicit, Pack = 16)] [StructLayout(LayoutKind.Explicit, Pack = 16)]
public unsafe struct M128 : IRegisterPresentation public unsafe struct M128 : IRegisterPresentation
{ {
[FieldOffset(0)] internal fixed byte Buffer[16]; [FieldOffset(0)] internal fixed byte Buffer[16];
[FieldOffset(0)] internal Vector128<float> Vec; [FieldOffset(0)] internal Vector128<float> Vec;
internal M128(Vector4 v) internal M128(Vector4 v)
{ {
Unsafe.SkipInit(out this); Unsafe.SkipInit(out this);
Unsafe.SkipInit(out Vec); Unsafe.SkipInit(out Vec);
Vec = v.AsVector128(); Vec = v.AsVector128();
} }
internal M128(Vector128<float> v) internal M128(Vector128<float> v)
{ {
Unsafe.SkipInit(out this); Unsafe.SkipInit(out this);
Vec = v; Vec = v;
} }
public ref T AsRef<T>() where T: unmanaged, INumber<T> public ref T AsRef<T>() where T: unmanaged, INumber<T>
{ {
return ref Unsafe.As<byte, T>(ref Buffer[0]); return ref Unsafe.As<byte, T>(ref Buffer[0]);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector4 AsVector4() public readonly Vector4 AsVector4()
{ {
return Vec.AsVector4(); return Vec.AsVector4();
} }
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T> public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
{ {
Vec = Vector128.Create<T>(value).AsSingle(); Vec = Vector128.Create<T>(value).AsSingle();
} }
public override readonly bool Equals([NotNullWhen(true)] object obj) public override readonly bool Equals([NotNullWhen(true)] object obj)
{ {
return obj is M128 other && this == other; return obj is M128 other && this == other;
} }
public override readonly int GetHashCode() public override readonly int GetHashCode()
{ {
return Vec.GetHashCode(); return Vec.GetHashCode();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Member<T>(int index) where T: unmanaged, INumber<T> public ref T Member<T>(int index) where T: unmanaged, INumber<T>
{ {
return ref Unsafe.Add(ref AsRef<T>(), index); return ref Unsafe.Add(ref AsRef<T>(), index);
} }
public override string ToString() public override string ToString()
{ {
return string.Empty; return string.Empty;
} }
public string ToString(int sample) public string ToString(int sample)
{ {
string str = string.Empty; string str = string.Empty;
for (int i = 15; i >= 0; i--) { for (int i = 15; i >= 0; i--) {
str += $"{Convert.ToString(Buffer[i], sample)} "; str += $"{Convert.ToString(Buffer[i], sample)} ";
} }
return str; return str;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(M128 left, M128 right) public static bool operator !=(M128 left, M128 right)
{ {
return left.Vec != right.Vec; return left.Vec != right.Vec;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(M128 left, M128 right) public static bool operator ==(M128 left, M128 right)
{ {
return left.Vec == right.Vec; return left.Vec == right.Vec;
} }
} }
} }
@@ -1,65 +1,65 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="M256" // file="M256"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.Mathematics namespace StgSharp.Mathematics
{ {
[StructLayout(LayoutKind.Explicit, Pack = 16)] [StructLayout(LayoutKind.Explicit, Pack = 16)]
public unsafe struct M256 : IRegisterPresentation public unsafe struct M256 : IRegisterPresentation
{ {
[FieldOffset(0)] internal fixed byte Buffer[32]; [FieldOffset(0)] internal fixed byte Buffer[32];
[FieldOffset(0)] internal Vector256<float> Vec; [FieldOffset(0)] internal Vector256<float> Vec;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T AsRef<T>() where T: unmanaged, INumber<T> public ref T AsRef<T>() where T: unmanaged, INumber<T>
{ {
return ref Unsafe.As<byte, T>(ref Buffer[0]); return ref Unsafe.As<byte, T>(ref Buffer[0]);
} }
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T> public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
{ {
Vec = Vector256.Create(value).AsSingle(); Vec = Vector256.Create(value).AsSingle();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Member<T>(int index) where T: unmanaged, INumber<T> public ref T Member<T>(int index) where T: unmanaged, INumber<T>
{ {
return ref Unsafe.Add(ref AsRef<T>(), index); return ref Unsafe.Add(ref AsRef<T>(), index);
} }
} }
} }
@@ -1,63 +1,63 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="M512" // file="M512"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.Mathematics namespace StgSharp.Mathematics
{ {
[StructLayout(LayoutKind.Explicit, Size = 64, Pack = 64)] [StructLayout(LayoutKind.Explicit, Size = 64, Pack = 64)]
public unsafe struct M512 : IRegisterPresentation public unsafe struct M512 : IRegisterPresentation
{ {
[FieldOffset(0)] private fixed byte _buffer[64]; [FieldOffset(0)] private fixed byte _buffer[64];
[FieldOffset(0)] private Vector512<float> Vec; [FieldOffset(0)] private Vector512<float> Vec;
public ref T AsRef<T>() where T: unmanaged, INumber<T> public ref T AsRef<T>() where T: unmanaged, INumber<T>
{ {
return ref Unsafe.As<byte, T>(ref _buffer[0]); return ref Unsafe.As<byte, T>(ref _buffer[0]);
} }
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T> public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
{ {
Vec = Vector512.Create(value).AsSingle(); Vec = Vector512.Create(value).AsSingle();
} }
public ref T Member<T>(int index) where T: unmanaged, INumber<T> public ref T Member<T>(int index) where T: unmanaged, INumber<T>
{ {
return ref Unsafe.As<byte, T>(ref _buffer[0]); return ref Unsafe.As<byte, T>(ref _buffer[0]);
} }
} }
} }
@@ -1,100 +1,100 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="M64" // file="M64"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
namespace StgSharp.Mathematics namespace StgSharp.Mathematics
{ {
[StructLayout(LayoutKind.Explicit, Pack = 8)] [StructLayout(LayoutKind.Explicit, Pack = 8)]
public unsafe struct M64 : IRegisterPresentation public unsafe struct M64 : IRegisterPresentation
{ {
[FieldOffset(0)] private fixed byte buffer[8]; [FieldOffset(0)] private fixed byte buffer[8];
[FieldOffset(0)] private ulong value; [FieldOffset(0)] private ulong value;
[FieldOffset(0)] private Vector64<float> vec; [FieldOffset(0)] private Vector64<float> vec;
public M64() public M64()
{ {
Unsafe.SkipInit(out this); Unsafe.SkipInit(out this);
Unsafe.SkipInit(out vec); Unsafe.SkipInit(out vec);
Unsafe.SkipInit(out value); Unsafe.SkipInit(out value);
} }
public ref T AsRef<T>() where T: unmanaged,INumber<T> public ref T AsRef<T>() where T: unmanaged,INumber<T>
{ {
return ref Unsafe.As<byte, T>(ref buffer[0]); return ref Unsafe.As<byte, T>(ref buffer[0]);
} }
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T> public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
{ {
vec = Vector64.Create(value).AsSingle(); vec = Vector64.Create(value).AsSingle();
} }
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return obj is M64 m64 && this == m64; return obj is M64 m64 && this == m64;
} }
public override readonly int GetHashCode() public override readonly int GetHashCode()
{ {
return value.GetHashCode(); return value.GetHashCode();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Member<T>(int index) where T: unmanaged, INumber<T> public ref T Member<T>(int index) where T: unmanaged, INumber<T>
{ {
return ref Unsafe.As<byte, T>(ref buffer[index * sizeof(T)]); return ref Unsafe.As<byte, T>(ref buffer[index * sizeof(T)]);
} }
public static bool operator !=(M64 left, M64 right) public static bool operator !=(M64 left, M64 right)
{ {
return !(left == right); return !(left == right);
} }
public static M64 operator <<(M64 value, int shift) public static M64 operator <<(M64 value, int shift)
{ {
M64 result = new M64(); M64 result = new M64();
result.value = value.value << shift; result.value = value.value << shift;
return result; return result;
} }
public static bool operator ==(M64 left, M64 right) public static bool operator ==(M64 left, M64 right)
{ {
return left.value == right.value; return left.value == right.value;
} }
public static M64 operator >>(M64 value, int shift) public static M64 operator >>(M64 value, int shift)
{ {
M64 result = new M64(); M64 result = new M64();
result.value = value.value << shift; result.value = value.value << shift;
return result; return result;
} }
} }
} }
+133 -133
View File
@@ -1,133 +1,133 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="DefaultLog" // file="DefaultLog"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
namespace StgSharp.Internal namespace StgSharp.Internal
{ {
internal static unsafe partial class DefaultLog internal static unsafe partial class DefaultLog
{ {
private const long MaxLogFileSize = 10 * 1024 * 1024; // 10 MB private const long MaxLogFileSize = 10 * 1024 * 1024; // 10 MB
private static LogType _currentLogLevel = LogType.Info; private static LogType _currentLogLevel = LogType.Info;
internal static readonly GCHandle ssc_logHandle = internal static readonly GCHandle ssc_logHandle =
GCHandle.Alloc(new byte[512], GCHandleType.Pinned); GCHandle.Alloc(new byte[512], GCHandleType.Pinned);
internal static SemaphoreSlim logSyncSemaphore = new(1, 1); internal static SemaphoreSlim logSyncSemaphore = new(1, 1);
internal static void InternalAppendLog( internal static void InternalAppendLog(
string log string log
) )
{ {
if (!File.Exists(Native.LogPath)) if (!File.Exists(Native.LogPath))
{ {
FileStream stream = File.Create(Native.LogPath); FileStream stream = File.Create(Native.LogPath);
stream.Close(); stream.Close();
} }
using (StreamWriter logStream = File.AppendText(Native.LogPath)) { using (StreamWriter logStream = File.AppendText(Native.LogPath)) {
logStream.WriteLine(log); logStream.WriteLine(log);
} }
} }
internal static void InternalWriteLog( internal static void InternalWriteLog(
string logLine, string logLine,
LogType logType LogType logType
) )
{ {
if (logType < _currentLogLevel) { if (logType < _currentLogLevel) {
return; return;
} }
logSyncSemaphore.Wait(); logSyncSemaphore.Wait();
try try
{ {
RotateLogFile(); RotateLogFile();
if (!File.Exists(Native.LogPath)) if (!File.Exists(Native.LogPath))
{ {
FileStream stream = File.Create(Native.LogPath); FileStream stream = File.Create(Native.LogPath);
stream.Close(); stream.Close();
} }
using (StreamWriter logStream = File.AppendText(Native.LogPath)) { using (StreamWriter logStream = File.AppendText(Native.LogPath)) {
logStream.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] [{logType}] {logLine}"); logStream.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] [{logType}] {logLine}");
} }
} }
finally finally
{ {
_ = logSyncSemaphore.Release(); _ = logSyncSemaphore.Release();
} }
} }
internal static void InternalWriteLog( internal static void InternalWriteLog(
string beforeTime, string beforeTime,
string logLine, string logLine,
LogType logType LogType logType
) )
{ {
logSyncSemaphore.Wait(); logSyncSemaphore.Wait();
if (!File.Exists(Native.LogPath)) if (!File.Exists(Native.LogPath))
{ {
FileStream stream = File.Create(Native.LogPath); FileStream stream = File.Create(Native.LogPath);
stream.Close(); stream.Close();
} }
using (StreamWriter logStream = File.AppendText(Native.LogPath)) using (StreamWriter logStream = File.AppendText(Native.LogPath))
{ {
logStream.WriteLine(beforeTime); logStream.WriteLine(beforeTime);
logStream.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] [{logType}] {logLine}"); logStream.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] [{logType}] {logLine}");
} }
_ = logSyncSemaphore.Release(1); _ = logSyncSemaphore.Release(1);
} }
internal static void SetLogLevel( internal static void SetLogLevel(
LogType logLevel LogType logLevel
) )
{ {
_currentLogLevel = logLevel; _currentLogLevel = logLevel;
} }
private static void RotateLogFile() private static void RotateLogFile()
{ {
if (File.Exists(Native.LogPath) && new FileInfo(Native.LogPath).Length > MaxLogFileSize) if (File.Exists(Native.LogPath) && new FileInfo(Native.LogPath).Length > MaxLogFileSize)
{ {
string backupFile = Native.LogPath + ".backup"; string backupFile = Native.LogPath + ".backup";
if (File.Exists(backupFile)) { if (File.Exists(backupFile)) {
File.Delete(backupFile); File.Delete(backupFile);
} }
File.Move(Native.LogPath, backupFile); File.Move(Native.LogPath, backupFile);
} }
} }
} }
} }
+48 -48
View File
@@ -1,48 +1,48 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="Native" // file="Native"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.Internal namespace StgSharp.Internal
{ {
internal static unsafe partial class Native internal static unsafe partial class Native
{ {
internal const string LibName = internal const string LibName =
"StgSharp.Native"; "StgSharp.Native";
internal const string LogPath = internal const string LogPath =
"SS_error.log"; "SS_error.log";
internal static IntPtr _libPtr = IntPtr.Zero; internal static IntPtr _libPtr = IntPtr.Zero;
} }
} }
+45 -45
View File
@@ -1,46 +1,46 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="public" // file="public"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp namespace StgSharp
{ {
public enum LogType public enum LogType
{ {
Error, Error,
Warning, Warning,
Info, Info,
InfoError, InfoError,
InfoWarning, InfoWarning,
} }
} }
@@ -1,91 +1,91 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="CrossFramePipelineNode" // file="CrossFramePipelineNode"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using StgSharp.PipeLine; using StgSharp.PipeLine;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.PipeLine namespace StgSharp.PipeLine
{ {
public class CrossFrameOperation : IConvertableToPipelineNode public class CrossFrameOperation : IConvertableToPipelineNode
{ {
private static string[] _inputName = [ "TaskIn" ]; private static string[] _inputName = [ "TaskIn" ];
private static string[] _outputName = [ "TaskOut" ]; private static string[] _outputName = [ "TaskOut" ];
private Action _mainExecution; private Action _mainExecution;
private int currentCount; private int currentCount;
private readonly int cycleCount; private readonly int cycleCount;
public CrossFrameOperation(Action startup, int cycleCount) public CrossFrameOperation(Action startup, int cycleCount)
{ {
MainExecution = startup; MainExecution = startup;
this.cycleCount = (cycleCount > 1) ? this.cycleCount = (cycleCount > 1) ?
cycleCount : cycleCount :
throw new ArgumentException( throw new ArgumentException(
$"{$"The {typeof(CrossFrameOperation).Name} crosses only one cycle, "}{$"please use a {typeof(PipelineNode).Name} instead."}"); $"{$"The {typeof(CrossFrameOperation).Name} crosses only one cycle, "}{$"please use a {typeof(PipelineNode).Name} instead."}");
this.cycleCount = cycleCount; this.cycleCount = cycleCount;
currentCount = cycleCount; currentCount = cycleCount;
} }
public Action MainExecution public Action MainExecution
{ {
get => _mainExecution; get => _mainExecution;
private protected set => _mainExecution = value; private protected set => _mainExecution = value;
} }
public IEnumerable<string> InputPortName => _inputName; public IEnumerable<string> InputPortName => _inputName;
public IEnumerable<string> OutputPortName => _outputName; public IEnumerable<string> OutputPortName => _outputName;
public PipelineNodeOperation Operation public PipelineNodeOperation Operation
{ {
get => NodeMain; get => NodeMain;
} }
public void NodeMain( public void NodeMain(
in Dictionary<string, PipelineNodeInPort> input, in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output) in Dictionary<string, PipelineNodeOutPort> output)
{ {
if (currentCount < cycleCount) if (currentCount < cycleCount)
{ {
Interlocked.Increment(ref currentCount); Interlocked.Increment(ref currentCount);
} else } else
{ {
Interlocked.Exchange(ref currentCount, 0); Interlocked.Exchange(ref currentCount, 0);
Task.Run(MainExecution); Task.Run(MainExecution);
} }
// Console.WriteLine(currentCount); // Console.WriteLine(currentCount);
PipelineNodeOutPort.SkipAll(output); PipelineNodeOutPort.SkipAll(output);
} }
} }
} }
@@ -1,82 +1,82 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="IConvertableToPipelineNode" // file="IConvertableToPipelineNode"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
namespace StgSharp.PipeLine namespace StgSharp.PipeLine
{ {
public delegate void PipelineNodeOperation( public delegate void PipelineNodeOperation(
in Dictionary<string, PipelineNodeInPort> input, in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output); in Dictionary<string, PipelineNodeOutPort> output);
public interface IConvertableToPipelineNode public interface IConvertableToPipelineNode
{ {
public IEnumerable<string> InputPortName { get; } public IEnumerable<string> InputPortName { get; }
public IEnumerable<string> OutputPortName { get; } public IEnumerable<string> OutputPortName { get; }
PipelineNodeOperation Operation { get; } PipelineNodeOperation Operation { get; }
void NodeMain( void NodeMain(
in Dictionary<string, PipelineNodeInPort> input, in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output); in Dictionary<string, PipelineNodeOutPort> output);
} }
internal class DefaultConvertableToBlueprintNode : IConvertableToPipelineNode internal class DefaultConvertableToBlueprintNode : IConvertableToPipelineNode
{ {
private string[] _input; private string[] _input;
private string[] _output; private string[] _output;
private PipelineNodeOperation _execution; private PipelineNodeOperation _execution;
public DefaultConvertableToBlueprintNode(PipelineNodeOperation execution, string[] input, string[] output) public DefaultConvertableToBlueprintNode(PipelineNodeOperation execution, string[] input, string[] output)
{ {
_input = input; _input = input;
_output = output; _output = output;
_execution = execution; _execution = execution;
} }
public IEnumerable<string> InputPortName => _input; public IEnumerable<string> InputPortName => _input;
public IEnumerable<string> OutputPortName => _output; public IEnumerable<string> OutputPortName => _output;
public PipelineNodeOperation Operation => _execution; public PipelineNodeOperation Operation => _execution;
public void NodeMain( public void NodeMain(
in Dictionary<string, PipelineNodeInPort> input, in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output) in Dictionary<string, PipelineNodeOutPort> output)
{ {
_execution.Invoke(input, output); _execution.Invoke(input, output);
} }
} }
} }
+242 -242
View File
@@ -1,242 +1,242 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="PipeLineRunner" // file="PipeLineRunner"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using StgSharp.Threading; using StgSharp.Threading;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.PipeLine namespace StgSharp.PipeLine
{ {
public static class PipeLineRunner public static class PipeLineRunner
{ {
private static NodeRunner[] allAvailableTask = CreateBlueprintRunnerArray(); private static NodeRunner[] allAvailableTask = CreateBlueprintRunnerArray();
private static CancellationTokenSource cts = new CancellationTokenSource(); private static CancellationTokenSource cts = new CancellationTokenSource();
private static ConcurrentStack<PipelineScheduler> pipelineInWaiting private static ConcurrentStack<PipelineScheduler> pipelineInWaiting
= new ConcurrentStack<PipelineScheduler>(); = new ConcurrentStack<PipelineScheduler>();
private static int _callingThread; private static int _callingThread;
private static SemaphoreSlim pipelineLock = new SemaphoreSlim(0, 1); private static SemaphoreSlim pipelineLock = new SemaphoreSlim(0, 1);
public static void Run(PipelineScheduler pipeline) public static void Run(PipelineScheduler pipeline)
{ {
// Console.WriteLine(); // Console.WriteLine();
if (pipeline == null) if (pipeline == null)
{ {
return; return;
} }
if (pipeline.IsRunning) { if (pipeline.IsRunning) {
throw new InvalidOperationException("Attempt to run a blueprint instance twice."); throw new InvalidOperationException("Attempt to run a blueprint instance twice.");
} }
if (!IsValidCalling()) { if (!IsValidCalling()) {
pipelineLock.Wait(); pipelineLock.Wait();
} }
if (InterruptAndRunNewPipeline(pipeline)) if (InterruptAndRunNewPipeline(pipeline))
{ {
while (InterruptAndRunNewPipeline(NodeRunner.CurrentPipeline!)) { } while (InterruptAndRunNewPipeline(NodeRunner.CurrentPipeline!)) { }
} }
pipelineLock.Release(); pipelineLock.Release();
Interlocked.Exchange(ref _callingThread, -1); Interlocked.Exchange(ref _callingThread, -1);
} }
public static void SetMaxTaskCount(int size) public static void SetMaxTaskCount(int size)
{ {
if (size < 1) { if (size < 1) {
size = 1; size = 1;
} }
if (allAvailableTask.Length >= size) if (allAvailableTask.Length >= size)
{ {
for (int i = size; i < allAvailableTask.Length; i++) for (int i = size; i < allAvailableTask.Length; i++)
{ {
if (allAvailableTask[i] != null) { if (allAvailableTask[i] != null) {
allAvailableTask[i].Cancel(); allAvailableTask[i].Cancel();
} }
} }
} }
Array.Resize(ref allAvailableTask, size); Array.Resize(ref allAvailableTask, size);
} }
public static void Terminate() public static void Terminate()
{ {
cts.Cancel(); cts.Cancel();
if (NodeRunner.CurrentPipeline is null) { if (NodeRunner.CurrentPipeline is null) {
return; return;
} }
NodeRunner.CurrentPipeline.Terminate(); NodeRunner.CurrentPipeline.Terminate();
foreach (NodeRunner task in allAvailableTask) { foreach (NodeRunner task in allAvailableTask) {
task.Cts.Cancel(); task.Cts.Cancel();
} }
foreach (PipelineScheduler bp in pipelineInWaiting) { foreach (PipelineScheduler bp in pipelineInWaiting) {
bp.Terminate(); bp.Terminate();
} }
pipelineInWaiting.Clear(); pipelineInWaiting.Clear();
} }
internal static bool InterruptAndRunNewPipeline(PipelineScheduler newPipeline) internal static bool InterruptAndRunNewPipeline(PipelineScheduler newPipeline)
{ {
ArgumentNullException.ThrowIfNull(newPipeline); ArgumentNullException.ThrowIfNull(newPipeline);
if ((newPipeline != NodeRunner.CurrentPipeline) && (NodeRunner.CurrentPipeline != null)) if ((newPipeline != NodeRunner.CurrentPipeline) && (NodeRunner.CurrentPipeline != null))
{ {
// if a running bp exists // if a running bp exists
pipelineInWaiting.Push(NodeRunner.CurrentPipeline); pipelineInWaiting.Push(NodeRunner.CurrentPipeline);
} }
// load new bp to current bp // load new bp to current bp
NodeRunner.CurrentPipeline = newPipeline; NodeRunner.CurrentPipeline = newPipeline;
Interlocked.Exchange(ref _callingThread, Environment.CurrentManagedThreadId); Interlocked.Exchange(ref _callingThread, Environment.CurrentManagedThreadId);
// run beginning node to init it first. // run beginning node to init it first.
if (!NodeRunner.CurrentPipeline.IsRunning) if (!NodeRunner.CurrentPipeline.IsRunning)
{ {
(NodeRunner.CurrentPipeline.BeginLayer as BeginningNode)!.Run(); (NodeRunner.CurrentPipeline.BeginLayer as BeginningNode)!.Run();
} }
// refresh all tasks // refresh all tasks
/* /*
for( int i = 0; i < allAvailableTask.Length; i++ ) for( int i = 0; i < allAvailableTask.Length; i++ )
{ {
NodeRunner item = allAvailableTask[ i ]; NodeRunner item = allAvailableTask[ i ];
if( ( item is null ) || item.IsCompleted ) { if( ( item is null ) || item.IsCompleted ) {
allAvailableTask[ i ] = NodeRunner.Run(); allAvailableTask[ i ] = NodeRunner.Run();
} }
} }
/**/ /**/
PipelineNode node; PipelineNode node;
while (!cts.Token.IsCancellationRequested && NodeRunner.CurrentPipeline.RequestNextMainThreadNode(out node)) while (!cts.Token.IsCancellationRequested && NodeRunner.CurrentPipeline.RequestNextMainThreadNode(out node))
{ {
node.Run(); node.Run();
if (!NodeRunner.CurrentPipeline.IsRunning) if (!NodeRunner.CurrentPipeline.IsRunning)
{ {
break; break;
} }
} }
while ((NodeRunner.CurrentPipeline != null) && while ((NodeRunner.CurrentPipeline != null) &&
NodeRunner.CurrentPipeline.IsRunning && NodeRunner.CurrentPipeline.IsRunning &&
!cts.Token.IsCancellationRequested && !cts.Token.IsCancellationRequested &&
NodeRunner.CurrentPipeline!.RequestNexConcurrentNode(out node)) NodeRunner.CurrentPipeline!.RequestNexConcurrentNode(out node))
{ {
node.Run(); node.Run();
if (!NodeRunner.CurrentPipeline!.IsRunning) if (!NodeRunner.CurrentPipeline!.IsRunning)
{ {
break; break;
} }
} }
(NodeRunner.CurrentPipeline!.EndLayer as EndingNode)!.Run(); (NodeRunner.CurrentPipeline!.EndLayer as EndingNode)!.Run();
bool isEmpty = pipelineInWaiting.TryPop(out PipelineScheduler? pipeline); bool isEmpty = pipelineInWaiting.TryPop(out PipelineScheduler? pipeline);
NodeRunner.CurrentPipeline = pipeline!; NodeRunner.CurrentPipeline = pipeline!;
return isEmpty; return isEmpty;
} }
/// <summary> /// <summary>
/// <para> Only the following occasions of calling are valid: </para> <para> 1. no bp is /// <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 /// 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> /// thread the current bp is running on.</para>
/// </summary> /// </summary>
internal static bool IsValidCalling() internal static bool IsValidCalling()
{ {
if ((NodeRunner.CurrentPipeline == null) || //no bp running at all if ((NodeRunner.CurrentPipeline == null) || //no bp running at all
((pipelineInWaiting.IsEmpty) && (NodeRunner.CurrentPipeline.IsRunning == false))) ((pipelineInWaiting.IsEmpty) && (NodeRunner.CurrentPipeline.IsRunning == false)))
{ {
// a bp is quitting // a bp is quitting
// in case no bp is running // in case no bp is running
return true; return true;
} }
int? id = Task.CurrentId; int? id = Task.CurrentId;
if (id is null) if (id is null)
{ {
// in case calling from main thread and current running bp // in case calling from main thread and current running bp
return _callingThread == Environment.CurrentManagedThreadId; return _callingThread == Environment.CurrentManagedThreadId;
} }
foreach (NodeRunner item in allAvailableTask) foreach (NodeRunner item in allAvailableTask)
{ {
if (item.Id == id) if (item.Id == id)
{ {
// if calling from any bp running task // if calling from any bp running task
return true; return true;
} }
} }
return false; return false;
} }
private static NodeRunner[] CreateBlueprintRunnerArray() private static NodeRunner[] CreateBlueprintRunnerArray()
{ {
int count = Environment.ProcessorCount; int count = Environment.ProcessorCount;
return new NodeRunner[ count < 2 ? count : count - 2]; return new NodeRunner[ count < 2 ? count : count - 2];
} }
} }
internal class NodeRunner : CancellableTask internal class NodeRunner : CancellableTask
{ {
private static volatile PipelineScheduler? _current; private static volatile PipelineScheduler? _current;
internal NodeRunner() internal NodeRunner()
: base() : base()
{ {
_startup = InternalRun; _startup = InternalRun;
} }
internal static PipelineScheduler? CurrentPipeline internal static PipelineScheduler? CurrentPipeline
{ {
get => _current; get => _current;
set => _current = value; set => _current = value;
} }
internal static NodeRunner Run() internal static NodeRunner Run()
{ {
NodeRunner ret = new NodeRunner NodeRunner ret = new NodeRunner
{ {
_startup = InternalRun _startup = InternalRun
}; };
ret._internalTask = Task.Run(ret.InternalAction); ret._internalTask = Task.Run(ret.InternalAction);
return ret; return ret;
} }
private static void InternalRun(CancellationToken token) private static void InternalRun(CancellationToken token)
{ {
PipelineNode item; PipelineNode item;
Console.WriteLine($"Global thread {Environment.CurrentManagedThreadId} starts."); Console.WriteLine($"Global thread {Environment.CurrentManagedThreadId} starts.");
while ((CurrentPipeline != null) && while ((CurrentPipeline != null) &&
CurrentPipeline.IsRunning && CurrentPipeline.IsRunning &&
!token.IsCancellationRequested && !token.IsCancellationRequested &&
CurrentPipeline.RequestNexConcurrentNode(out item)) { CurrentPipeline.RequestNexConcurrentNode(out item)) {
item.Run(); item.Run();
} }
} }
}//------------------------------------- End of Class ------------------------------------------ }//------------------------------------- End of Class ------------------------------------------
} }
+114 -114
View File
@@ -1,114 +1,114 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="PipelineMarshal" // file="PipelineMarshal"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.PipeLine namespace StgSharp.PipeLine
{ {
public static class PipelineMarshal public static class PipelineMarshal
{ {
public static IEnumerable<PipelineNode> GetNodeSortedEnumeration(PipelineScheduler scheduler) public static IEnumerable<PipelineNode> GetNodeSortedEnumeration(PipelineScheduler scheduler)
{ {
ArgumentNullException.ThrowIfNull(scheduler); ArgumentNullException.ThrowIfNull(scheduler);
return new PipelineNodeEnumerator(scheduler) return new PipelineNodeEnumerator(scheduler)
{ {
_count = scheduler._allNode.Count, _count = scheduler._allNode.Count,
}; };
} }
private class PipelineNodeEnumerator : ICollection<PipelineNode> private class PipelineNodeEnumerator : ICollection<PipelineNode>
{ {
private readonly List<List<PipelineNode>> _mt, _c; private readonly List<List<PipelineNode>> _mt, _c;
public int _count; public int _count;
public PipelineNodeEnumerator(PipelineScheduler scheduler) public PipelineNodeEnumerator(PipelineScheduler scheduler)
{ {
if (!scheduler.TryGetAllNode(out _mt, out _c)) { if (!scheduler.TryGetAllNode(out _mt, out _c)) {
throw new InvalidOperationException("Binded scheduler is not sorted."); throw new InvalidOperationException("Binded scheduler is not sorted.");
} }
} }
public bool IsReadOnly => true; public bool IsReadOnly => true;
public int Count => _count; public int Count => _count;
public IEnumerator<PipelineNode> GetEnumerator() public IEnumerator<PipelineNode> GetEnumerator()
{ {
for (int i = 1; i < _mt.Count; i++) for (int i = 1; i < _mt.Count; i++)
{ {
foreach (PipelineNode node in _mt[i]) { foreach (PipelineNode node in _mt[i]) {
yield return node; yield return node;
} }
foreach (PipelineNode node in _c[i]) { foreach (PipelineNode node in _c[i]) {
yield return node; yield return node;
} }
} }
} }
void ICollection<PipelineNode>.Add(PipelineNode item) void ICollection<PipelineNode>.Add(PipelineNode item)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
void ICollection<PipelineNode>.Clear() void ICollection<PipelineNode>.Clear()
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
bool ICollection<PipelineNode>.Contains(PipelineNode item) bool ICollection<PipelineNode>.Contains(PipelineNode item)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
void ICollection<PipelineNode>.CopyTo(PipelineNode[] array, int arrayIndex) void ICollection<PipelineNode>.CopyTo(PipelineNode[] array, int arrayIndex)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
bool ICollection<PipelineNode>.Remove(PipelineNode item) bool ICollection<PipelineNode>.Remove(PipelineNode item)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
return GetEnumerator(); return GetEnumerator();
} }
} }
} }
} }
+266 -266
View File
@@ -1,266 +1,266 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="PipelineNode" // file="PipelineNode"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.PipeLine namespace StgSharp.PipeLine
{ {
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <risk> /// <risk>
/// Reset pipeline has risk, this operation is a direct operation to port in current node, the /// 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. /// port on the other node cannot get the change.
/// </risk> /// </risk>
#pragma warning disable CA1036 #pragma warning disable CA1036
public class PipelineNode public class PipelineNode
#pragma warning restore CA1036 #pragma warning restore CA1036
{ {
private static readonly Dictionary<string, PipelineConnector> _emptyLayer = []; private static readonly Dictionary<string, PipelineConnector> _emptyLayer = [];
private static PipelineNode _emptyNode = new PipelineNode( private static PipelineNode _emptyNode = new PipelineNode(
PipelineNodeLabel.Empty, null!, false); PipelineNodeLabel.Empty, null!, false);
private IConvertableToPipelineNode _mainOperation; private IConvertableToPipelineNode _mainOperation;
private PipelineNodeLabel _label; private PipelineNodeLabel _label;
private protected bool _nativeRequested; private protected bool _nativeRequested;
private protected Dictionary<string, PipelineNodeInPort> _input; private protected Dictionary<string, PipelineNodeInPort> _input;
private protected Dictionary<string, PipelineNodeOutPort> _output; private protected Dictionary<string, PipelineNodeOutPort> _output;
private protected int _level, _count; private protected int _level, _count;
internal PipelineNode(PipelineNodeLabel label, IConvertableToPipelineNode node, bool isNative) internal PipelineNode(PipelineNodeLabel label, IConvertableToPipelineNode node, bool isNative)
{ {
_label = label; _label = label;
_nativeRequested = isNative; _nativeRequested = isNative;
_mainOperation = node; _mainOperation = node;
InputPorts = new(); InputPorts = new();
OutputPorts = new(); OutputPorts = new();
foreach (string port in node.InputPortName) { foreach (string port in node.InputPortName) {
InputPorts.Add(port, new PipelineNodeInPort(this, port)); InputPorts.Add(port, new PipelineNodeInPort(this, port));
} }
foreach (string port in node.OutputPortName) { foreach (string port in node.OutputPortName) {
OutputPorts.Add(port, new PipelineNodeOutPort(this, port)); OutputPorts.Add(port, new PipelineNodeOutPort(this, port));
} }
} }
#pragma warning disable CS8618 #pragma warning disable CS8618
internal PipelineNode( internal PipelineNode(
PipelineNodeOperation operation, PipelineNodeOperation operation,
PipelineNodeLabel name, PipelineNodeLabel name,
bool isNative, bool isNative,
string[] inputNames, string[] inputNames,
string[] outputNames) string[] outputNames)
#pragma warning restore CS8618 #pragma warning restore CS8618
{ {
if ((inputNames == null) || (inputNames.Length == 0)) { if ((inputNames == null) || (inputNames.Length == 0)) {
throw new ArgumentNullException(nameof(inputNames)); throw new ArgumentNullException(nameof(inputNames));
} }
if ((outputNames == null) || (outputNames.Length == 0)) { if ((outputNames == null) || (outputNames.Length == 0)) {
throw new ArgumentNullException(nameof(outputNames)); throw new ArgumentNullException(nameof(outputNames));
} }
_label = name; _label = name;
_nativeRequested = isNative; _nativeRequested = isNative;
if (operation == null) { if (operation == null) {
operation = DefaultOperation; operation = DefaultOperation;
} }
_mainOperation = new DefaultConvertableToBlueprintNode( _mainOperation = new DefaultConvertableToBlueprintNode(
operation, inputNames, outputNames); operation, inputNames, outputNames);
InputPorts = new Dictionary<string, PipelineNodeInPort>(); InputPorts = new Dictionary<string, PipelineNodeInPort>();
OutputPorts = new Dictionary<string, PipelineNodeOutPort>(); OutputPorts = new Dictionary<string, PipelineNodeOutPort>();
foreach (string port in inputNames) { foreach (string port in inputNames) {
InputPorts.Add(port, new PipelineNodeInPort(this, port)); InputPorts.Add(port, new PipelineNodeInPort(this, port));
} }
foreach (string port in outputNames) { foreach (string port in outputNames) {
OutputPorts.Add(port, new PipelineNodeOutPort(this, port)); OutputPorts.Add(port, new PipelineNodeOutPort(this, port));
} }
} }
public bool IsNative public bool IsNative
{ {
get => _nativeRequested; get => _nativeRequested;
internal set => _nativeRequested = value; internal set => _nativeRequested = value;
} }
public static Dictionary<string, PipelineConnector> EmptyPortLayer => _emptyLayer; public static Dictionary<string, PipelineConnector> EmptyPortLayer => _emptyLayer;
public IConvertableToPipelineNode NodeOperation => _mainOperation; public IConvertableToPipelineNode NodeOperation => _mainOperation;
public int Level public int Level
{ {
get => _level; get => _level;
private set => _level = value; private set => _level = value;
} }
public static PipelineNode Empty => _emptyNode; public static PipelineNode Empty => _emptyNode;
public PipelineNodeLabel Label => _label; public PipelineNodeLabel Label => _label;
internal Dictionary<string, PipelineNodeInPort> InputPorts internal Dictionary<string, PipelineNodeInPort> InputPorts
{ {
get => _input; get => _input;
private set => _input = value; private set => _input = value;
} }
internal Dictionary<string, PipelineNodeOutPort> OutputPorts internal Dictionary<string, PipelineNodeOutPort> OutputPorts
{ {
get => _output; get => _output;
private set => _output = value; private set => _output = value;
} }
internal HashSet<PipelineNode> Previous { get; } = []; internal HashSet<PipelineNode> Previous { get; } = [];
internal HashSet<PipelineNode> Next { get; } = []; internal HashSet<PipelineNode> Next { get; } = [];
internal IConvertableToPipelineNode Value => _mainOperation; internal IConvertableToPipelineNode Value => _mainOperation;
internal int RemainingIncreaseCount => Previous.Count - _count; internal int RemainingIncreaseCount => Previous.Count - _count;
public static void Connect(PipelineNode front, string frontNodePort, string backNodePort, PipelineNode back) public static void Connect(PipelineNode front, string frontNodePort, string backNodePort, PipelineNode back)
{ {
ArgumentNullException.ThrowIfNull(front); ArgumentNullException.ThrowIfNull(front);
ArgumentNullException.ThrowIfNull(back); ArgumentNullException.ThrowIfNull(back);
if (!front.OutputPorts if (!front.OutputPorts
.TryGetValue(frontNodePort, .TryGetValue(frontNodePort,
out PipelineNodeOutPort? frontPort)) { out PipelineNodeOutPort? frontPort)) {
throw new ArgumentOutOfRangeException($"Cannot find port named {frontNodePort}"); throw new ArgumentOutOfRangeException($"Cannot find port named {frontNodePort}");
} }
if (!back.InputPorts.TryGetValue(backNodePort, out PipelineNodeInPort? backPort)) { if (!back.InputPorts.TryGetValue(backNodePort, out PipelineNodeInPort? backPort)) {
throw new ArgumentOutOfRangeException($"Cannot find port named {backNodePort}"); throw new ArgumentOutOfRangeException($"Cannot find port named {backNodePort}");
} }
backPort.Connect(frontPort); backPort.Connect(frontPort);
frontPort.Connect(backPort); frontPort.Connect(backPort);
} }
public IEnumerator<PipelineNode> GetFormerNodes() public IEnumerator<PipelineNode> GetFormerNodes()
{ {
foreach (PipelineNodeInPort port in InputPorts.Values) foreach (PipelineNodeInPort port in InputPorts.Values)
{ {
IEnumerator<PipelineNode> enumerator = port.GetFormerNodes(); IEnumerator<PipelineNode> enumerator = port.GetFormerNodes();
while (enumerator.MoveNext()) { while (enumerator.MoveNext()) {
yield return enumerator.Current; yield return enumerator.Current;
} }
} }
} }
public virtual void Run() public virtual void Run()
{ {
try try
{ {
// Console.WriteLine(_label); // Console.WriteLine(_label);
PipelineNodeInPort.WaitAll(InputPorts); PipelineNodeInPort.WaitAll(InputPorts);
if (_mainOperation != null) { if (_mainOperation != null) {
_mainOperation.NodeMain(in _input, in _output); _mainOperation.NodeMain(in _input, in _output);
} }
} }
catch (Exception) catch (Exception)
{ {
throw; throw;
} }
} }
internal bool TryIncreaseLevelTo(int level) internal bool TryIncreaseLevelTo(int level)
{ {
_count++; _count++;
if (_count > Previous.Count) { if (_count > Previous.Count) {
return false; return false;
} }
Level = int.Max(level, this.Level); Level = int.Max(level, this.Level);
return true; return true;
} }
protected static void DefaultOperation( protected static void DefaultOperation(
in Dictionary<string, PipelineNodeInPort> input, in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output) in Dictionary<string, PipelineNodeOutPort> output)
{ {
PipelineNodeOutPort.SkipAll(output); PipelineNodeOutPort.SkipAll(output);
} }
#region port operation #region port operation
public PipelineNodeInPort GetInputPort(string lable) public PipelineNodeInPort GetInputPort(string lable)
{ {
if (!InputPorts.TryGetValue(lable, out PipelineNodeInPort? ret)) { if (!InputPorts.TryGetValue(lable, out PipelineNodeInPort? ret)) {
throw new ArgumentOutOfRangeException( throw new ArgumentOutOfRangeException(
$"Cannot find the interface named {lable} in node {Label}."); $"Cannot find the interface named {lable} in node {Label}.");
} }
return ret; return ret;
} }
public PipelineNodeOutPort GetOutputPort(string lable) public PipelineNodeOutPort GetOutputPort(string lable)
{ {
if (!OutputPorts.TryGetValue(lable, out PipelineNodeOutPort? ret)) { if (!OutputPorts.TryGetValue(lable, out PipelineNodeOutPort? ret)) {
throw new ArgumentOutOfRangeException( throw new ArgumentOutOfRangeException(
$"Cannot find the interface named {lable} in node {Label}."); $"Cannot find the interface named {lable} in node {Label}.");
} }
return ret; return ret;
} }
public PipelineNodeInPort? DefineCertainInputPort(string label) public PipelineNodeInPort? DefineCertainInputPort(string label)
{ {
if (InputPorts.ContainsKey(label)) { if (InputPorts.ContainsKey(label)) {
return null; return null;
} }
PipelineNodeInPort port = new(this, label); PipelineNodeInPort port = new(this, label);
InputPorts[label] = port; InputPorts[label] = port;
return port; return port;
} }
public PipelineNodeOutPort? DefineCertainOutputPort(string label) public PipelineNodeOutPort? DefineCertainOutputPort(string label)
{ {
if (OutputPorts.ContainsKey(label)) { if (OutputPorts.ContainsKey(label)) {
return null; return null;
} }
PipelineNodeOutPort port = new(this, label); PipelineNodeOutPort port = new(this, label);
OutputPorts[label] = port; OutputPorts[label] = port;
return port; return port;
} }
#endregion #endregion
}//--------------------------------- end of class ----------------------------------- }//--------------------------------- end of class -----------------------------------
public class PipelineNodeRunException : Exception public class PipelineNodeRunException : Exception
{ {
public PipelineNodeRunException() { } public PipelineNodeRunException() { }
public PipelineNodeRunException(Exception ex) : base(string.Empty, ex) { } public PipelineNodeRunException(Exception ex) : base(string.Empty, ex) { }
public PipelineNodeRunException(string message) : base(message) { } public PipelineNodeRunException(string message) : base(message) { }
public PipelineNodeRunException(string message, Exception innerException) : base(message, innerException) { } public PipelineNodeRunException(string message, Exception innerException) : base(message, innerException) { }
} }
} }
+80 -80
View File
@@ -1,80 +1,80 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="PipelineNodeLabel" // file="PipelineNodeLabel"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.PipeLine namespace StgSharp.PipeLine
{ {
public abstract record class PipelineNodeLabel public abstract record class PipelineNodeLabel
{ {
public static PipelineNodeLabel Empty => PipelineNodeLabelEmpty.Only; public static PipelineNodeLabel Empty => PipelineNodeLabelEmpty.Only;
public abstract int ComputeHashCode(); public abstract int ComputeHashCode();
public override int GetHashCode() public override int GetHashCode()
{ {
return ComputeHashCode(); return ComputeHashCode();
} }
} }
public record class PipelineNodeStringLabel : PipelineNodeLabel public record class PipelineNodeStringLabel : PipelineNodeLabel
{ {
public PipelineNodeStringLabel(string name) public PipelineNodeStringLabel(string name)
{ {
Name = name; Name = name;
} }
public string Name { get; } public string Name { get; }
public override int ComputeHashCode() public override int ComputeHashCode()
{ {
return string.GetHashCode(Name); return string.GetHashCode(Name);
} }
} }
internal record class PipelineNodeLabelEmpty : PipelineNodeLabel internal record class PipelineNodeLabelEmpty : PipelineNodeLabel
{ {
public static PipelineNodeLabelEmpty Only { get; } = new PipelineNodeLabelEmpty(); public static PipelineNodeLabelEmpty Only { get; } = new PipelineNodeLabelEmpty();
public override int ComputeHashCode() public override int ComputeHashCode()
{ {
return 0; return 0;
} }
} }
} }
+187 -187
View File
@@ -1,187 +1,187 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="PipelineNodePort" // file="PipelineNodePort"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.ExceptionServices; using System.Runtime.ExceptionServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.PipeLine namespace StgSharp.PipeLine
{ {
public class PipelineNodeInPort public class PipelineNodeInPort
{ {
private HashSet<PipelineNodeOutPort> _linkerSet = new HashSet<PipelineNodeOutPort>(); private HashSet<PipelineNodeOutPort> _linkerSet = new HashSet<PipelineNodeOutPort>();
public PipelineNodeInPort(PipelineNode container, string label) public PipelineNodeInPort(PipelineNode container, string label)
{ {
Container = container; Container = container;
Label = label; Label = label;
} }
public IEnumerable<PipelineNodeOutPort> LinkedPorts => _linkerSet; public IEnumerable<PipelineNodeOutPort> LinkedPorts => _linkerSet;
public PipelineNode Container { get; init; } public PipelineNode Container { get; init; }
public SemaphoreSlim Waiter { get; init; } public SemaphoreSlim Waiter { get; init; }
public string Label { get; init; } public string Label { get; init; }
internal HashSet<PipelineNodeOutPort> Linkers => _linkerSet; internal HashSet<PipelineNodeOutPort> Linkers => _linkerSet;
public void Connect(PipelineNodeOutPort port) public void Connect(PipelineNodeOutPort port)
{ {
ArgumentNullException.ThrowIfNull(port); ArgumentNullException.ThrowIfNull(port);
if (_linkerSet.Add(port)) if (_linkerSet.Add(port))
{ {
port.Linkers.Add(this); port.Linkers.Add(this);
Container.Previous.Add(port.Container); Container.Previous.Add(port.Container);
port.Container.Next.Add(Container); port.Container.Next.Add(Container);
} }
} }
public IEnumerator<PipelineNode> GetFormerNodes() public IEnumerator<PipelineNode> GetFormerNodes()
{ {
foreach (PipelineNodeOutPort port in _linkerSet) { foreach (PipelineNodeOutPort port in _linkerSet) {
yield return port.Container; yield return port.Container;
} }
} }
public void Wait() public void Wait()
{ {
PipelineNodeOutPort.WaitAll(_linkerSet); PipelineNodeOutPort.WaitAll(_linkerSet);
} }
public static void WaitAll(IDictionary<string, PipelineNodeInPort> ports) public static void WaitAll(IDictionary<string, PipelineNodeInPort> ports)
{ {
if (ports is null) { if (ports is null) {
return; return;
} }
foreach (PipelineNodeInPort port in ports.Values) { foreach (PipelineNodeInPort port in ports.Values) {
port.Wait(); port.Wait();
} }
} }
public static void WaitAll(IEnumerable<PipelineNodeInPort> ports) public static void WaitAll(IEnumerable<PipelineNodeInPort> ports)
{ {
if (ports is null) { if (ports is null) {
return; return;
} }
foreach (PipelineNodeInPort port in ports) { foreach (PipelineNodeInPort port in ports) {
port.Wait(); port.Wait();
} }
} }
} }
public class PipelineNodeOutPort public class PipelineNodeOutPort
{ {
private HashSet<PipelineNodeInPort> _linkerSet = new HashSet<PipelineNodeInPort>(); private HashSet<PipelineNodeInPort> _linkerSet = new HashSet<PipelineNodeInPort>();
private volatile int _passCount; private volatile int _passCount;
private SemaphoreSlim formerCompleteSemaphore; private SemaphoreSlim formerCompleteSemaphore;
public PipelineNodeOutPort(PipelineNode container, string label) public PipelineNodeOutPort(PipelineNode container, string label)
{ {
Container = container; Container = container;
Label = label; Label = label;
formerCompleteSemaphore = new SemaphoreSlim(initialCount:0, maxCount:1); formerCompleteSemaphore = new SemaphoreSlim(initialCount:0, maxCount:1);
} }
public PipelineNode Container { get; init; } public PipelineNode Container { get; init; }
public string Label { get; init; } public string Label { get; init; }
internal HashSet<PipelineNodeInPort> Linkers => _linkerSet; internal HashSet<PipelineNodeInPort> Linkers => _linkerSet;
private SemaphoreSlim Waiter => formerCompleteSemaphore; private SemaphoreSlim Waiter => formerCompleteSemaphore;
public void Connect(PipelineNodeInPort port) public void Connect(PipelineNodeInPort port)
{ {
ArgumentNullException.ThrowIfNull(port); ArgumentNullException.ThrowIfNull(port);
if (_linkerSet.Add(port)) if (_linkerSet.Add(port))
{ {
port.Linkers.Add(this); port.Linkers.Add(this);
Container.Next.Add(port.Container); Container.Next.Add(port.Container);
port.Container.Previous.Add(Container); port.Container.Previous.Add(Container);
} }
} }
public void Release() public void Release()
{ {
if (_passCount == 0) if (_passCount == 0)
{ {
Waiter.Release(); Waiter.Release();
} else } else
{ {
return; return;
} }
} }
public static void SkipAll(Dictionary<string, PipelineNodeOutPort> ports) public static void SkipAll(Dictionary<string, PipelineNodeOutPort> ports)
{ {
if (ports is null) { if (ports is null) {
return; return;
} }
foreach (PipelineNodeOutPort port in ports.Values) { foreach (PipelineNodeOutPort port in ports.Values) {
port.Release(); port.Release();
} }
} }
public void TransmitValue(IPipeLineConnectionPayload value) public void TransmitValue(IPipeLineConnectionPayload value)
{ {
foreach (PipelineNodeInPort item in _linkerSet) { } foreach (PipelineNodeInPort item in _linkerSet) { }
} }
public static void WaitAll(IEnumerable<PipelineNodeOutPort> ports) public static void WaitAll(IEnumerable<PipelineNodeOutPort> ports)
{ {
if (ports is null) { if (ports is null) {
return; return;
} }
foreach (PipelineNodeOutPort port in ports) { foreach (PipelineNodeOutPort port in ports) {
port.Wait(); port.Wait();
} }
} }
internal void Wait() internal void Wait()
{ {
Waiter.Wait(); Waiter.Wait();
if (_passCount >= _linkerSet.Count) if (_passCount >= _linkerSet.Count)
{ {
Interlocked.Exchange(ref _passCount, 0); Interlocked.Exchange(ref _passCount, 0);
} else } else
{ {
Interlocked.Increment(ref _passCount); Interlocked.Increment(ref _passCount);
Waiter.Release(); Waiter.Release();
} }
} }
} }
} }
+295 -295
View File
@@ -1,295 +1,295 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="PipelineScheduler" // file="PipelineScheduler"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Xml; using System.Xml;
using System.Xml.Linq; using System.Xml.Linq;
namespace StgSharp.PipeLine namespace StgSharp.PipeLine
{ {
/// <summary> /// <summary>
/// A sets of complex logic designed for running in multi threads environment. Warning: do not /// 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. /// run a <see cref="PipelineScheduler" /> instance in multi threads more than once.
/// </summary> /// </summary>
public partial class PipelineScheduler : IConvertableToPipelineNode public partial class PipelineScheduler : IConvertableToPipelineNode
{ {
private BeginningNode _beginning; private BeginningNode _beginning;
private bool _isAvailable = false; private bool _isAvailable = false;
private EndingNode _ending; private EndingNode _ending;
private int mtLevel, cLevel,mtIndex, cIndex; private int mtLevel, cLevel,mtIndex, cIndex;
private List<List<PipelineNode>> _concurrentNodes, _mainThreadNodes ; private List<List<PipelineNode>> _concurrentNodes, _mainThreadNodes ;
private SemaphoreSlim runningStat,_concurrentLock = new SemaphoreSlim(1, 1); private SemaphoreSlim runningStat,_concurrentLock = new SemaphoreSlim(1, 1);
internal Dictionary<PipelineNodeLabel, PipelineNode> _allNode; internal Dictionary<PipelineNodeLabel, PipelineNode> _allNode;
public PipelineScheduler() public PipelineScheduler()
{ {
_allNode = new Dictionary<PipelineNodeLabel, PipelineNode>(); _allNode = new Dictionary<PipelineNodeLabel, PipelineNode>();
runningStat = new SemaphoreSlim(1, 1); runningStat = new SemaphoreSlim(1, 1);
_beginning = new BeginningNode(this); _beginning = new BeginningNode(this);
_ending = new EndingNode(this); _ending = new EndingNode(this);
} }
/**/ /**/
public PipelineNode this[PipelineNodeLabel label] public PipelineNode this[PipelineNodeLabel label]
{ {
get { return _allNode[label]; } get { return _allNode[label]; }
} }
public bool IsRunning public bool IsRunning
{ {
get => runningStat.CurrentCount == 0; get => runningStat.CurrentCount == 0;
} }
public IEnumerable<string> InputPortName public IEnumerable<string> InputPortName
{ {
get => _beginning.InputPorts.Keys; get => _beginning.InputPorts.Keys;
} }
public IEnumerable<string> OutputPortName public IEnumerable<string> OutputPortName
{ {
get => _beginning.OutputPorts.Keys; get => _beginning.OutputPorts.Keys;
} }
public PipelineNode BeginLayer => _beginning; public PipelineNode BeginLayer => _beginning;
public PipelineNode EndLayer => _ending; public PipelineNode EndLayer => _ending;
public PipelineNodeOperation Operation public PipelineNodeOperation Operation
{ {
get => (this as IConvertableToPipelineNode).NodeMain; get => (this as IConvertableToPipelineNode).NodeMain;
} }
/**/ /**/
internal SemaphoreSlim RunningStat internal SemaphoreSlim RunningStat
{ {
get => runningStat; get => runningStat;
} }
public void AddNode(PipelineNode node, bool isNative) public void AddNode(PipelineNode node, bool isNative)
{ {
ArgumentNullException.ThrowIfNull(node); ArgumentNullException.ThrowIfNull(node);
node.IsNative = isNative; node.IsNative = isNative;
_allNode.TryAdd(node.Label, node); _allNode.TryAdd(node.Label, node);
} }
public void ArrangeNodes() public void ArrangeNodes()
{ {
if (_isAvailable) { if (_isAvailable) {
return; return;
} }
int maxLevel = 1; int maxLevel = 1;
Queue<PipelineNode> completedNodes = new Queue<PipelineNode>(); Queue<PipelineNode> completedNodes = new Queue<PipelineNode>();
foreach (PipelineNode item in _beginning.Next) foreach (PipelineNode item in _beginning.Next)
{ {
item.TryIncreaseLevelTo(1); item.TryIncreaseLevelTo(1);
completedNodes.Enqueue(item); completedNodes.Enqueue(item);
} }
// calc level of nodes O(V) // calc level of nodes O(V)
while (completedNodes.Count > 0) while (completedNodes.Count > 0)
{ {
PipelineNode u = completedNodes.Dequeue(); PipelineNode u = completedNodes.Dequeue();
foreach (PipelineNode v in u.Next) foreach (PipelineNode v in u.Next)
{ {
if (!v.TryIncreaseLevelTo(u.Level + 1)) { if (!v.TryIncreaseLevelTo(u.Level + 1)) {
throw new InvalidOperationException("Ring in DAG detected."); throw new InvalidOperationException("Ring in DAG detected.");
} }
if (v.RemainingIncreaseCount == 0) if (v.RemainingIncreaseCount == 0)
{ {
completedNodes.Enqueue(v); completedNodes.Enqueue(v);
maxLevel = int.Max(maxLevel, v.Level); maxLevel = int.Max(maxLevel, v.Level);
} }
} }
} }
// bucket sort of nodes, O(E) // bucket sort of nodes, O(E)
_concurrentNodes = new List<List<PipelineNode>>(maxLevel + 1); _concurrentNodes = new List<List<PipelineNode>>(maxLevel + 1);
_mainThreadNodes = new List<List<PipelineNode>>(maxLevel + 1); _mainThreadNodes = new List<List<PipelineNode>>(maxLevel + 1);
for (int i = 0; i <= maxLevel; i++) for (int i = 0; i <= maxLevel; i++)
{ {
_concurrentNodes.Add([]); _concurrentNodes.Add([]);
_mainThreadNodes.Add([]); _mainThreadNodes.Add([]);
} }
foreach (PipelineNode item in _allNode.Values) foreach (PipelineNode item in _allNode.Values)
{ {
if (item.IsNative) if (item.IsNative)
{ {
_mainThreadNodes[item.Level].Add(item); _mainThreadNodes[item.Level].Add(item);
} else } else
{ {
_concurrentNodes[item.Level].Add(item); _concurrentNodes[item.Level].Add(item);
} }
} }
foreach (List<PipelineNode> item in _concurrentNodes) { foreach (List<PipelineNode> item in _concurrentNodes) {
item.TrimExcess(); item.TrimExcess();
} }
foreach (List<PipelineNode> item in _mainThreadNodes) { foreach (List<PipelineNode> item in _mainThreadNodes) {
item.TrimExcess(); item.TrimExcess();
} }
_isAvailable = true; _isAvailable = true;
} }
public PipelineNode Create(PipelineNodeLabel label, [NotNull] IConvertableToPipelineNode body) public PipelineNode Create(PipelineNodeLabel label, [NotNull] IConvertableToPipelineNode body)
{ {
if (_allNode.TryGetValue(label, out PipelineNode? node)) { if (_allNode.TryGetValue(label, out PipelineNode? node)) {
throw new InvalidOperationException("Label has exist in scheduler."); throw new InvalidOperationException("Label has exist in scheduler.");
} }
node = new PipelineNode(label, body, false); node = new PipelineNode(label, body, false);
_allNode.Add(label, node); _allNode.Add(label, node);
return node; return node;
} }
public PipelineNode GetNode(PipelineNodeLabel label) public PipelineNode GetNode(PipelineNodeLabel label)
{ {
if (_allNode.TryGetValue(label, out PipelineNode? node)) if (_allNode.TryGetValue(label, out PipelineNode? node))
{ {
return node; return node;
} else } else
{ {
throw new KeyNotFoundException(); throw new KeyNotFoundException();
} }
} }
public void LinkToInput(PipelineNodeInPort port) public void LinkToInput(PipelineNodeInPort port)
{ {
ArgumentNullException.ThrowIfNull(port); ArgumentNullException.ThrowIfNull(port);
PipelineNodeOutPort p = _beginning.DefaultOut; PipelineNodeOutPort p = _beginning.DefaultOut;
p.Connect(port); p.Connect(port);
} }
public void LinkToInput(PipelineNodeInPort port, string inputPortName) public void LinkToInput(PipelineNodeInPort port, string inputPortName)
{ {
ArgumentNullException.ThrowIfNull(port); ArgumentNullException.ThrowIfNull(port);
PipelineNodeOutPort p = _beginning.DefineCertainOutputPort(inputPortName); PipelineNodeOutPort p = _beginning.DefineCertainOutputPort(inputPortName);
p.Connect(port); p.Connect(port);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetInput(params (string label, IPipeLineConnectionPayload arg)[] parameters) public void SetInput(params (string label, IPipeLineConnectionPayload arg)[] parameters)
{ {
_beginning.SetInputData(parameters); _beginning.SetInputData(parameters);
} }
public void Terminate() public void Terminate()
{ {
_concurrentLock.Wait(); _concurrentLock.Wait();
(mtLevel, cLevel, mtIndex, cIndex) = (int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue); (mtLevel, cLevel, mtIndex, cIndex) = (int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue);
_concurrentLock.Release(); _concurrentLock.Release();
} }
public bool TryGetAllNode( public bool TryGetAllNode(
out List<List<PipelineNode>> mainThreadNodes, out List<List<PipelineNode>> mainThreadNodes,
out List<List<PipelineNode>> concurrentNodes) out List<List<PipelineNode>> concurrentNodes)
{ {
if (_isAvailable) if (_isAvailable)
{ {
mainThreadNodes = _mainThreadNodes; mainThreadNodes = _mainThreadNodes;
concurrentNodes = _concurrentNodes; concurrentNodes = _concurrentNodes;
return true; return true;
} }
mainThreadNodes = null; mainThreadNodes = null;
concurrentNodes = null; concurrentNodes = null;
return false; return false;
} }
internal bool RequestNexConcurrentNode(out PipelineNode node) internal bool RequestNexConcurrentNode(out PipelineNode node)
{ {
int index, level; int index, level;
if (cLevel >= _concurrentNodes.Count) if (cLevel >= _concurrentNodes.Count)
{ {
node = null!; node = null!;
return false; return false;
} }
_concurrentLock.Wait(); _concurrentLock.Wait();
while (cIndex >= _concurrentNodes[cLevel].Count) while (cIndex >= _concurrentNodes[cLevel].Count)
{ {
Interlocked.Increment(ref cLevel); Interlocked.Increment(ref cLevel);
Interlocked.Exchange(ref cIndex, 0); Interlocked.Exchange(ref cIndex, 0);
if (cLevel >= _concurrentNodes.Count) if (cLevel >= _concurrentNodes.Count)
{ {
node = null!; node = null!;
return false; return false;
} }
} }
index = cIndex; index = cIndex;
level = cLevel; level = cLevel;
Interlocked.Increment(ref cIndex); Interlocked.Increment(ref cIndex);
_concurrentLock.Release(); _concurrentLock.Release();
node = _concurrentNodes[level][index]; node = _concurrentNodes[level][index];
return true; return true;
} }
internal bool RequestNextMainThreadNode(out PipelineNode node) internal bool RequestNextMainThreadNode(out PipelineNode node)
{ {
if (mtLevel >= _mainThreadNodes.Count) if (mtLevel >= _mainThreadNodes.Count)
{ {
node = null!; node = null!;
return false; return false;
} }
while (mtIndex >= _mainThreadNodes[cLevel].Count) while (mtIndex >= _mainThreadNodes[cLevel].Count)
{ {
Interlocked.Increment(ref mtLevel); Interlocked.Increment(ref mtLevel);
Interlocked.Exchange(ref mtIndex, 0); Interlocked.Exchange(ref mtIndex, 0);
if (mtLevel >= _mainThreadNodes.Count) if (mtLevel >= _mainThreadNodes.Count)
{ {
node = null!; node = null!;
return false; return false;
} }
} }
node = _mainThreadNodes[mtLevel][mtIndex]; node = _mainThreadNodes[mtLevel][mtIndex];
return true; return true;
} }
internal void ResetIndex() internal void ResetIndex()
{ {
(mtLevel, cLevel, mtIndex, cIndex) = (0, 0, 0, 0); (mtLevel, cLevel, mtIndex, cIndex) = (0, 0, 0, 0);
_concurrentLock.Release(); _concurrentLock.Release();
} }
void IConvertableToPipelineNode.NodeMain( void IConvertableToPipelineNode.NodeMain(
in Dictionary<string, PipelineNodeInPort> input, in Dictionary<string, PipelineNodeInPort> input,
in Dictionary<string, PipelineNodeOutPort> output) in Dictionary<string, PipelineNodeOutPort> output)
{ {
PipeLineRunner.Run(this); PipeLineRunner.Run(this);
PipelineNodeOutPort.SkipAll(output); PipelineNodeOutPort.SkipAll(output);
} }
~PipelineScheduler() ~PipelineScheduler()
{ {
runningStat.Dispose(); runningStat.Dispose();
} }
} }
} }
+123 -123
View File
@@ -1,123 +1,123 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="PortArgs" // file="PortArgs"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Security.Authentication.ExtendedProtection; using System.Security.Authentication.ExtendedProtection;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.PipeLine namespace StgSharp.PipeLine
{ {
public class PipelineConnector public class PipelineConnector
{ {
private protected IPipeLineConnectionPayload args; private protected IPipeLineConnectionPayload args;
private protected PipelineNodeInPort output; private protected PipelineNodeInPort output;
private protected PipelineNodeOutPort input; private protected PipelineNodeOutPort input;
private protected SemaphoreSlim formerCompleteSemaphore; private protected SemaphoreSlim formerCompleteSemaphore;
public PipelineConnector(PipelineNodeOutPort former, PipelineNodeInPort after) public PipelineConnector(PipelineNodeOutPort former, PipelineNodeInPort after)
{ {
this.input = former; this.input = former;
this.output = after; this.output = after;
formerCompleteSemaphore = new SemaphoreSlim(initialCount:0, maxCount:1); formerCompleteSemaphore = new SemaphoreSlim(initialCount:0, maxCount:1);
} }
public IPipeLineConnectionPayload Args public IPipeLineConnectionPayload Args
{ {
get => args; get => args;
set => args = value; set => args = value;
} }
public PipelineNodeInPort Output => output; public PipelineNodeInPort Output => output;
public PipelineNodeOutPort Input => input; public PipelineNodeOutPort Input => input;
public static void SkipAll(Dictionary<string, PipelineConnector> ports) public static void SkipAll(Dictionary<string, PipelineConnector> ports)
{ {
if (ports == null) { if (ports == null) {
return; return;
} }
foreach (KeyValuePair<string, PipelineConnector> item in ports) foreach (KeyValuePair<string, PipelineConnector> item in ports)
{ {
if (item.Value != null) { if (item.Value != null) {
item.Value.CompleteAndSkip(); item.Value.CompleteAndSkip();
} }
} }
} }
public static void WaitAll(Dictionary<string, PipelineConnector> ports) public static void WaitAll(Dictionary<string, PipelineConnector> ports)
{ {
if (ports == null) { if (ports == null) {
return; return;
} }
foreach (KeyValuePair<string, PipelineConnector> item in ports) foreach (KeyValuePair<string, PipelineConnector> item in ports)
{ {
if (item.Value != null) { if (item.Value != null) {
item.Value.WaitForStart(); item.Value.WaitForStart();
} }
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual void WaitForStart() public virtual void WaitForStart()
{ {
// Console.WriteLine($"Waiting \t{input.ContextName}\t->\t{output.ContextName}"); // Console.WriteLine($"Waiting \t{input.ContextName}\t->\t{output.ContextName}");
formerCompleteSemaphore.Wait(); formerCompleteSemaphore.Wait();
} }
~PipelineConnector() ~PipelineConnector()
{ {
formerCompleteSemaphore.Dispose(); formerCompleteSemaphore.Dispose();
} }
#region pipline op #region pipline op
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CompleteAndSkip() public void CompleteAndSkip()
{ {
// Console.WriteLine($"Complete\t{input.Name}\t->\t{output.Name}"); // Console.WriteLine($"Complete\t{input.Name}\t->\t{output.Name}");
formerCompleteSemaphore.Release(); formerCompleteSemaphore.Release();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CompleteAndWriteData(IPipeLineConnectionPayload args) public void CompleteAndWriteData(IPipeLineConnectionPayload args)
{ {
this.args = args; this.args = args;
CompleteAndSkip(); CompleteAndSkip();
} }
#endregion #endregion
} }
public abstract class IPipeLineConnectionPayload { } public abstract class IPipeLineConnectionPayload { }
} }
+92 -92
View File
@@ -1,93 +1,93 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="SideNode" // file="SideNode"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Security.Principal; using System.Security.Principal;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.PipeLine namespace StgSharp.PipeLine
{ {
internal sealed class BeginningNode : PipelineNode internal sealed class BeginningNode : PipelineNode
{ {
public const string OutName = "default"; public const string OutName = "default";
private PipelineScheduler _bp; private PipelineScheduler _bp;
public BeginningNode(PipelineScheduler bp) public BeginningNode(PipelineScheduler bp)
: base( : base(
DefaultOperation, new PipelineNodeStringLabel("BeginningNode"), true, [ "default" ],[ OutName ]) DefaultOperation, new PipelineNodeStringLabel("BeginningNode"), true, [ "default" ],[ OutName ])
{ {
_bp = bp; _bp = bp;
} }
public PipelineNodeOutPort DefaultOut => OutputPorts[OutName]; public PipelineNodeOutPort DefaultOut => OutputPorts[OutName];
public override void Run() public override void Run()
{ {
_bp.RunningStat.Wait(); _bp.RunningStat.Wait();
PipelineNodeOutPort.SkipAll(_output); PipelineNodeOutPort.SkipAll(_output);
} }
internal void SetInputData(IEnumerable<(string, IPipeLineConnectionPayload)> data) internal void SetInputData(IEnumerable<(string, IPipeLineConnectionPayload)> data)
{ {
if (data is null) { if (data is null) {
return; return;
} }
foreach ((string, IPipeLineConnectionPayload) item in data) { foreach ((string, IPipeLineConnectionPayload) item in data) {
OutputPorts[item.Item1].TransmitValue(item.Item2); OutputPorts[item.Item1].TransmitValue(item.Item2);
} }
} }
} }
internal sealed class EndingNode : PipelineNode internal sealed class EndingNode : PipelineNode
{ {
private PipelineScheduler _bp; private PipelineScheduler _bp;
public EndingNode(PipelineScheduler bp) public EndingNode(PipelineScheduler bp)
: base( : base(
DefaultOperation, name:new PipelineNodeStringLabel("EndingNode"), true, [ "default" ],[ "default" ]) DefaultOperation, name:new PipelineNodeStringLabel("EndingNode"), true, [ "default" ],[ "default" ])
{ {
_bp = bp; _bp = bp;
} }
public override void Run() public override void Run()
{ {
PipelineNodeInPort.WaitAll(_input); PipelineNodeInPort.WaitAll(_input);
_bp.ResetIndex(); _bp.ResetIndex();
_bp.RunningStat.Release(); _bp.RunningStat.Release();
} }
} }
} }
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. --> <!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Configuration>Release</Configuration> <Configuration>Release</Configuration>
<Platform>x64</Platform> <Platform>x64</Platform>
<PublishDir>C:\Users\Nitlo\OneDrive - MSFT\桌面\新建文件夹</PublishDir> <PublishDir>C:\Users\Nitlo\OneDrive - MSFT\桌面\新建文件夹</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol> <PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId> <_TargetId>Folder</_TargetId>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained> <SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile> <PublishSingleFile>true</PublishSingleFile>
<PublishReadyToRun>true</PublishReadyToRun> <PublishReadyToRun>true</PublishReadyToRun>
<PublishTrimmed>true</PublishTrimmed> <PublishTrimmed>true</PublishTrimmed>
</PropertyGroup> </PropertyGroup>
</Project> </Project>
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. --> <!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<History>False|2025-08-24T07:15:39.9650284Z||;</History> <History>False|2025-08-24T07:15:39.9650284Z||;</History>
<LastFailureDetails /> <LastFailureDetails />
</PropertyGroup> </PropertyGroup>
</Project> </Project>
+56 -56
View File
@@ -1,56 +1,56 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="ISimpleStateMachine" // file="ISimpleStateMachine"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.State namespace StgSharp.State
{ {
public interface ISimpleStateMachine<TStateNode, TStateLabel, TStateContext> public interface ISimpleStateMachine<TStateNode, TStateLabel, TStateContext>
where TStateNode : SimpleStateMachineNode<TStateNode, TStateLabel, TStateContext> where TStateNode : SimpleStateMachineNode<TStateNode, TStateLabel, TStateContext>
where TStateLabel : unmanaged, IEquatable<TStateLabel> where TStateLabel : unmanaged, IEquatable<TStateLabel>
where TStateContext : StateContext where TStateContext : StateContext
{ {
TStateNode this[ TStateNode this[
TStateLabel label TStateLabel label
] { get; } ] { get; }
ref readonly TStateNode GetCurrentStateNode(); ref readonly TStateNode GetCurrentStateNode();
void Reset(); void Reset();
bool TryMoveNext( bool TryMoveNext(
TStateContext ctx TStateContext ctx
); );
} }
} }
+60 -60
View File
@@ -1,60 +1,60 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="SimpleStateMachineNode" // file="SimpleStateMachineNode"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.State namespace StgSharp.State
{ {
public abstract class SimpleStateMachineNode<TSelf, TLabel, TStateContext> public abstract class SimpleStateMachineNode<TSelf, TLabel, TStateContext>
where TSelf : SimpleStateMachineNode<TSelf, TLabel, TStateContext> where TSelf : SimpleStateMachineNode<TSelf, TLabel, TStateContext>
where TLabel : unmanaged, IEquatable<TLabel> where TLabel : unmanaged, IEquatable<TLabel>
where TStateContext : StateContext where TStateContext : StateContext
{ {
private readonly List<int> _jumpTable = []; private readonly List<int> _jumpTable = [];
public ReadOnlySpan<int> AvailableNodes => CollectionsMarshal.AsSpan(_jumpTable); public ReadOnlySpan<int> AvailableNodes => CollectionsMarshal.AsSpan(_jumpTable);
public abstract bool IsValidateFrom( public abstract bool IsValidateFrom(
TSelf former, TSelf former,
TStateContext context TStateContext context
); );
public abstract bool TryTransaction( public abstract bool TryTransaction(
ReadOnlySpan<TSelf> nodeSpan, ReadOnlySpan<TSelf> nodeSpan,
TStateContext context, TStateContext context,
out TLabel label out TLabel label
); );
} }
} }
+40 -40
View File
@@ -1,40 +1,40 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="StateContext.cs" // file="StateContext.cs"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person // Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation // obtaining a copy of this software and associated documentation
// files (the “Software”), to deal in the Software without restriction, // files (the “Software”), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, // including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, // publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so, // and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions: // subject to the following conditions:
// //
// The above copyright notice and // The above copyright notice and
// this permission notice shall be included in all copies // this permission notice shall be included in all copies
// or substantial portions of the Software. // or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED “AS IS”, // THE SOFTWARE IS PROVIDED “AS IS”,
// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // 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, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH // ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.State namespace StgSharp.State
{ {
public abstract class StateContext { } public abstract class StateContext { }
} }
+29 -29
View File
@@ -1,30 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<IsNativeHost>true</IsNativeHost> <IsNativeHost>true</IsNativeHost>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(IsWindows)'=='true'"> <ItemGroup Condition="'$(IsWindows)'=='true'">
<ProjectReference Include="..\StgSharp.Native\$(NativeLibName).vcxproj"> <ProjectReference Include="..\StgSharp.Native\$(NativeLibName).vcxproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly> <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties> <SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
<GlobalPropertiesToRemove>TargetFramework</GlobalPropertiesToRemove> <GlobalPropertiesToRemove>TargetFramework</GlobalPropertiesToRemove>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" /> <PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Remove="Graphics\**" /> <Compile Remove="Graphics\**" />
<EmbeddedResource Remove="Graphics\**" /> <EmbeddedResource Remove="Graphics\**" />
<None Remove="Graphics\**" /> <None Remove="Graphics\**" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="HighPerformance\RegisterAbstraction\" /> <Folder Include="HighPerformance\RegisterAbstraction\" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+5 -5
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<_LastSelectedProfileId>D:\NitloadIndustry\研究项目\1-Flagship\TH2020\ProjectFile\内核源代码\StgSharp.Common\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId> <_LastSelectedProfileId>D:\NitloadIndustry\研究项目\1-Flagship\TH2020\ProjectFile\内核源代码\StgSharp.Common\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
</PropertyGroup> </PropertyGroup>
</Project> </Project>
@@ -1,366 +1,366 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="AdaptiveBackoffStrategy" // file="AdaptiveBackoffStrategy"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
/// <summary> /// <summary>
/// Efficient adaptive backoff strategy that dynamically selects optimal backoff methods based on /// Efficient adaptive backoff strategy that dynamically selects optimal backoff methods based on
/// system load and success rate /// system load and success rate
/// </summary> /// </summary>
public struct AdaptiveBackoffStrategy public struct AdaptiveBackoffStrategy
{ {
private const int EVALUATION_INTERVAL_MS = 2000; // Global re-evaluation interval 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 EXPLORATION_PROBABILITY = 10; // 10% probability to explore non-optimal strategies
private const int FAST_CONVERGENCE_THRESHOLD = 1000; // Fast convergence threshold private const int FAST_CONVERGENCE_THRESHOLD = 1000; // Fast convergence threshold
// Configuration constants // Configuration constants
private const int INITIAL_SPINWAIT_THRESHOLD = 8; // Cheap spin count for first few SpinWait iterations 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 private const int MIN_SAMPLES_FOR_EVALUATION = 100; // Minimum sample count before starting evaluation
// Global shared state (all threads share learning results) // Global shared state (all threads share learning results)
private static readonly PerformanceStats[] s_globalStats = new PerformanceStats[3]; private static readonly PerformanceStats[] s_globalStats = new PerformanceStats[3];
private static volatile BackoffMethod s_globalBestMethod; private static volatile BackoffMethod s_globalBestMethod;
private static volatile int s_evaluationInProgress; private static volatile int s_evaluationInProgress;
private static long s_lastGlobalEvaluationTicks; private static long s_lastGlobalEvaluationTicks;
private BackoffMethod _currentMethod; private BackoffMethod _currentMethod;
private int _consecutiveFailures; private int _consecutiveFailures;
private int _localAttempts; private int _localAttempts;
// Instance state // Instance state
private SpinWait _spinWait; private SpinWait _spinWait;
private uint _fastRng; // Lightweight pseudo-random number generator private uint _fastRng; // Lightweight pseudo-random number generator
// Static constructor: initialize optimal strategy based on system characteristics // Static constructor: initialize optimal strategy based on system characteristics
static AdaptiveBackoffStrategy() static AdaptiveBackoffStrategy()
{ {
// Based on your benchmark results: // Based on your benchmark results:
// - Low core count (<= 8): SpinWait has clear advantage // - Low core count (<= 8): SpinWait has clear advantage
// - Medium core count (<= 16): Close performance, slightly favor SpinWait // - Medium core count (<= 16): Close performance, slightly favor SpinWait
// - High core count (>= 20): Sleep0 has significant advantage under high concurrency // - High core count (>= 20): Sleep0 has significant advantage under high concurrency
int logicalCores = Environment.ProcessorCount; int logicalCores = Environment.ProcessorCount;
if (logicalCores <= 8) if (logicalCores <= 8)
{ {
s_globalBestMethod = BackoffMethod.SpinWait; s_globalBestMethod = BackoffMethod.SpinWait;
} else if (logicalCores <= 16) } else if (logicalCores <= 16)
{ {
s_globalBestMethod = BackoffMethod.SpinWait; // Default conservative choice s_globalBestMethod = BackoffMethod.SpinWait; // Default conservative choice
} else } else
{ {
// High core count systems, based on your 64-thread benchmark results // High core count systems, based on your 64-thread benchmark results
s_globalBestMethod = BackoffMethod.Sleep0; s_globalBestMethod = BackoffMethod.Sleep0;
} }
s_lastGlobalEvaluationTicks = Environment.TickCount64; s_lastGlobalEvaluationTicks = Environment.TickCount64;
// Initialize statistics array // Initialize statistics array
for (int i = 0; i < s_globalStats.Length; i++) for (int i = 0; i < s_globalStats.Length; i++)
{ {
s_globalStats[i].Reset(); s_globalStats[i].Reset();
} }
} }
/// <summary> /// <summary>
/// Gets the currently used backoff method (for debugging/monitoring) /// Gets the currently used backoff method (for debugging/monitoring)
/// </summary> /// </summary>
public readonly BackoffMethod CurrentMethod => _currentMethod; public readonly BackoffMethod CurrentMethod => _currentMethod;
/// <summary> /// <summary>
/// Gets the globally considered optimal backoff method /// Gets the globally considered optimal backoff method
/// </summary> /// </summary>
public static BackoffMethod GlobalBestMethod => s_globalBestMethod; public static BackoffMethod GlobalBestMethod => s_globalBestMethod;
/// <summary> /// <summary>
/// Creates a new adaptive backoff strategy instance /// Creates a new adaptive backoff strategy instance
/// </summary> /// </summary>
public static AdaptiveBackoffStrategy Create() public static AdaptiveBackoffStrategy Create()
{ {
return new AdaptiveBackoffStrategy return new AdaptiveBackoffStrategy
{ {
_spinWait = default, _spinWait = default,
_currentMethod = s_globalBestMethod, _currentMethod = s_globalBestMethod,
_consecutiveFailures = 0, _consecutiveFailures = 0,
_localAttempts = 0, _localAttempts = 0,
_fastRng = (uint)(Environment.TickCount64 ^ Thread.CurrentThread.ManagedThreadId * 0x9E3779B9U) _fastRng = (uint)(Environment.TickCount64 ^ Thread.CurrentThread.ManagedThreadId * 0x9E3779B9U)
}; };
} }
/// <summary> /// <summary>
/// Gets global statistics for the specified strategy (for monitoring) /// Gets global statistics for the specified strategy (for monitoring)
/// </summary> /// </summary>
public static (long attempts, long successes, double successRate) GetGlobalStats(BackoffMethod method) public static (long attempts, long successes, double successRate) GetGlobalStats(BackoffMethod method)
{ {
PerformanceStats stats = s_globalStats[(int)method]; PerformanceStats stats = s_globalStats[(int)method];
return (stats.Attempts, stats.Successes, stats.SuccessRate); return (stats.Attempts, stats.Successes, stats.SuccessRate);
} }
/// <summary> /// <summary>
/// Called after CAS failure, performs backoff and may switch strategies /// Called after CAS failure, performs backoff and may switch strategies
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnFailureAndBackoff() public void OnFailureAndBackoff()
{ {
_consecutiveFailures++; _consecutiveFailures++;
// Record failure // Record failure
s_globalStats[(int)_currentMethod].RecordAttempt(success:false); s_globalStats[(int)_currentMethod].RecordAttempt(success:false);
// Periodically re-evaluate global optimal strategy // Periodically re-evaluate global optimal strategy
if (ShouldEvaluateGlobalStrategy()) if (ShouldEvaluateGlobalStrategy())
{ {
EvaluateAndUpdateGlobalStrategy(); EvaluateAndUpdateGlobalStrategy();
} }
// Decide whether to switch strategies based on failure count and current strategy // Decide whether to switch strategies based on failure count and current strategy
if (_consecutiveFailures >= INITIAL_SPINWAIT_THRESHOLD && ShouldSwitchStrategy()) if (_consecutiveFailures >= INITIAL_SPINWAIT_THRESHOLD && ShouldSwitchStrategy())
{ {
SwitchToOptimalStrategy(); SwitchToOptimalStrategy();
} }
// Perform the actual backoff operation // Perform the actual backoff operation
PerformBackoff(); PerformBackoff();
} }
/// <summary> /// <summary>
/// Called after CAS success, resets state /// Called after CAS success, resets state
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnSuccess() public void OnSuccess()
{ {
// Record success // Record success
s_globalStats[(int)_currentMethod].RecordAttempt(success:true); s_globalStats[(int)_currentMethod].RecordAttempt(success:true);
// Reset state // Reset state
_spinWait = default; _spinWait = default;
_consecutiveFailures = 0; _consecutiveFailures = 0;
// Fast convergence: tend to keep current strategy when continuously successful // Fast convergence: tend to keep current strategy when continuously successful
if (_localAttempts < FAST_CONVERGENCE_THRESHOLD) if (_localAttempts < FAST_CONVERGENCE_THRESHOLD)
{ {
_localAttempts++; _localAttempts++;
} }
} }
/// <summary> /// <summary>
/// Evaluates and updates the global optimal strategy /// Evaluates and updates the global optimal strategy
/// </summary> /// </summary>
private static void EvaluateAndUpdateGlobalStrategy() private static void EvaluateAndUpdateGlobalStrategy()
{ {
// Use CAS to ensure only one thread performs evaluation // Use CAS to ensure only one thread performs evaluation
if (Interlocked.CompareExchange(ref s_evaluationInProgress, 1, 0) != 0) if (Interlocked.CompareExchange(ref s_evaluationInProgress, 1, 0) != 0)
{ {
return; return;
} }
try try
{ {
long now = Environment.TickCount64; long now = Environment.TickCount64;
// Double-check to avoid duplicate evaluation // Double-check to avoid duplicate evaluation
if (now - Volatile.Read(ref s_lastGlobalEvaluationTicks) <= EVALUATION_INTERVAL_MS) if (now - Volatile.Read(ref s_lastGlobalEvaluationTicks) <= EVALUATION_INTERVAL_MS)
{ {
return; return;
} }
BackoffMethod bestMethod = s_globalBestMethod; BackoffMethod bestMethod = s_globalBestMethod;
double bestSuccessRate = -1.0; double bestSuccessRate = -1.0;
// Find the strategy with the highest success rate // Find the strategy with the highest success rate
for (int i = 0; i < s_globalStats.Length; i++) for (int i = 0; i < s_globalStats.Length; i++)
{ {
PerformanceStats stats = s_globalStats[i]; PerformanceStats stats = s_globalStats[i];
if (stats.Attempts >= MIN_SAMPLES_FOR_EVALUATION) if (stats.Attempts >= MIN_SAMPLES_FOR_EVALUATION)
{ {
double successRate = stats.SuccessRate; double successRate = stats.SuccessRate;
if (successRate > bestSuccessRate) if (successRate > bestSuccessRate)
{ {
bestSuccessRate = successRate; bestSuccessRate = successRate;
bestMethod = (BackoffMethod)i; bestMethod = (BackoffMethod)i;
} }
} }
} }
// Update global optimal strategy // Update global optimal strategy
s_globalBestMethod = bestMethod; s_globalBestMethod = bestMethod;
Volatile.Write(ref s_lastGlobalEvaluationTicks, now); Volatile.Write(ref s_lastGlobalEvaluationTicks, now);
// Reset statistics (maintain exploratory behavior) // Reset statistics (maintain exploratory behavior)
for (int i = 0; i < s_globalStats.Length; i++) for (int i = 0; i < s_globalStats.Length; i++)
{ {
s_globalStats[i].Reset(); s_globalStats[i].Reset();
} }
} }
finally finally
{ {
Volatile.Write(ref s_evaluationInProgress, 0); Volatile.Write(ref s_evaluationInProgress, 0);
} }
} }
/// <summary> /// <summary>
/// Fast pseudo-random number generator (LCG) /// Fast pseudo-random number generator (LCG)
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private uint NextRandom() private uint NextRandom()
{ {
_fastRng = _fastRng * 1103515245U + 12345U; _fastRng = _fastRng * 1103515245U + 12345U;
return _fastRng; return _fastRng;
} }
/// <summary> /// <summary>
/// Performs the actual backoff operation /// Performs the actual backoff operation
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.NoInlining)] // Avoid inlining to reduce hot path code size [MethodImpl(MethodImplOptions.NoInlining)] // Avoid inlining to reduce hot path code size
private void PerformBackoff() private void PerformBackoff()
{ {
switch (_currentMethod) switch (_currentMethod)
{ {
case BackoffMethod.SpinWait: case BackoffMethod.SpinWait:
if (_consecutiveFailures <= INITIAL_SPINWAIT_THRESHOLD) if (_consecutiveFailures <= INITIAL_SPINWAIT_THRESHOLD)
{ {
_spinWait.SpinOnce(sleep1Threshold:-1); _spinWait.SpinOnce(sleep1Threshold:-1);
} else if (_spinWait.NextSpinWillYield) } else if (_spinWait.NextSpinWillYield)
{ {
int yields = (int)(NextRandom() & 7) + 1; // 1-8 times int yields = (int)(NextRandom() & 7) + 1; // 1-8 times
for (int i = 0; i < yields; i++) { for (int i = 0; i < yields; i++) {
_ = Thread.Yield(); _ = Thread.Yield();
} }
} else } else
{ {
_spinWait.SpinOnce(sleep1Threshold:-1); _spinWait.SpinOnce(sleep1Threshold:-1);
} }
break; break;
case BackoffMethod.Yield: case BackoffMethod.Yield:
_ = Thread.Yield(); _ = Thread.Yield();
break; break;
case BackoffMethod.Sleep0: case BackoffMethod.Sleep0:
Thread.Sleep(0); Thread.Sleep(0);
break; break;
default: default:
break; break;
} }
} }
/// <summary> /// <summary>
/// Checks whether global strategy evaluation should be performed /// Checks whether global strategy evaluation should be performed
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool ShouldEvaluateGlobalStrategy() private static bool ShouldEvaluateGlobalStrategy()
{ {
long now = Environment.TickCount64; long now = Environment.TickCount64;
return now - Volatile.Read(ref s_lastGlobalEvaluationTicks) > EVALUATION_INTERVAL_MS; return now - Volatile.Read(ref s_lastGlobalEvaluationTicks) > EVALUATION_INTERVAL_MS;
} }
/// <summary> /// <summary>
/// Determines whether strategy should be switched /// Determines whether strategy should be switched
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool ShouldSwitchStrategy() private bool ShouldSwitchStrategy()
{ {
// Fast convergence phase: reduce strategy switching // Fast convergence phase: reduce strategy switching
if (_localAttempts < FAST_CONVERGENCE_THRESHOLD && _consecutiveFailures < 32) if (_localAttempts < FAST_CONVERGENCE_THRESHOLD && _consecutiveFailures < 32)
{ {
return false; return false;
} }
// Small probability exploration: occasionally try other strategies even when current is optimal // Small probability exploration: occasionally try other strategies even when current is optimal
return NextRandom() % 100 < EXPLORATION_PROBABILITY || _currentMethod != s_globalBestMethod; return NextRandom() % 100 < EXPLORATION_PROBABILITY || _currentMethod != s_globalBestMethod;
} }
/// <summary> /// <summary>
/// Switches to the currently considered optimal strategy /// Switches to the currently considered optimal strategy
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SwitchToOptimalStrategy() private void SwitchToOptimalStrategy()
{ {
BackoffMethod targetMethod; BackoffMethod targetMethod;
// Most of the time choose the globally optimal // Most of the time choose the globally optimal
if (NextRandom() % 100 >= EXPLORATION_PROBABILITY) if (NextRandom() % 100 >= EXPLORATION_PROBABILITY)
{ {
targetMethod = s_globalBestMethod; targetMethod = s_globalBestMethod;
} else } else
{ {
// Small probability random exploration // Small probability random exploration
targetMethod = (BackoffMethod)(NextRandom() % 3); targetMethod = (BackoffMethod)(NextRandom() % 3);
} }
if (targetMethod != _currentMethod) if (targetMethod != _currentMethod)
{ {
_currentMethod = targetMethod; _currentMethod = targetMethod;
_spinWait = default; // Reset SpinWait state when switching strategies _spinWait = default; // Reset SpinWait state when switching strategies
_localAttempts = 0; _localAttempts = 0;
} }
} }
public enum BackoffMethod : byte public enum BackoffMethod : byte
{ {
SpinWait = 0, SpinWait = 0,
Yield = 1, Yield = 1,
Sleep0 = 2 Sleep0 = 2
} }
private struct PerformanceStats private struct PerformanceStats
{ {
public long Attempts; public long Attempts;
public long LastResetTicks; public long LastResetTicks;
public long Successes; public long Successes;
public readonly double SuccessRate public readonly double SuccessRate
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Attempts == 0 ? 0.0 : (double)Successes / Attempts; get => Attempts == 0 ? 0.0 : (double)Successes / Attempts;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RecordAttempt(bool success) public void RecordAttempt(bool success)
{ {
_ = Interlocked.Increment(ref Attempts); _ = Interlocked.Increment(ref Attempts);
if (success) { if (success) {
_ = Interlocked.Increment(ref Successes); _ = Interlocked.Increment(ref Successes);
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset() public void Reset()
{ {
Attempts = 0; Attempts = 0;
Successes = 0; Successes = 0;
LastResetTicks = Environment.TickCount64; LastResetTicks = Environment.TickCount64;
} }
} }
} }
+125 -125
View File
@@ -1,125 +1,125 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="BufferExpansionLock" // file="BufferExpansionLock"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.Threading namespace StgSharp.Threading
{ {
/// <summary> /// <summary>
/// Provide a lock for managing concurrent access to a buffer that can be expanded and use a /// Provide a lock for managing concurrent access to a buffer that can be expanded and use a
/// common two-pase-copy expanding mechanism. /// common two-pase-copy expanding mechanism.
/// </summary> /// </summary>
public class BufferExpansionLock : IDisposable public class BufferExpansionLock : IDisposable
{ {
private static readonly ThreadLocal<int> Count = new(() => 0); private static readonly ThreadLocal<int> Count = new(() => 0);
private readonly ReaderWriterLockSlim _copyLock; private readonly ReaderWriterLockSlim _copyLock;
private readonly ReaderWriterLockSlim _expansionLock; private readonly ReaderWriterLockSlim _expansionLock;
public BufferExpansionLock() public BufferExpansionLock()
{ {
_expansionLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); _expansionLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
_copyLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); _copyLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
} }
public bool IsThreadReadingMetaData public bool IsThreadReadingMetaData
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _expansionLock.IsReadLockHeld; get => _expansionLock.IsReadLockHeld;
} }
public void Dispose() public void Dispose()
{ {
_expansionLock?.Dispose(); _expansionLock?.Dispose();
_copyLock?.Dispose(); _copyLock?.Dispose();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnterBufferCopy() public void EnterBufferCopy()
{ {
_copyLock.EnterWriteLock(); _copyLock.EnterWriteLock();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnterBufferRead() public void EnterBufferRead()
{ {
int c = Count.Value; int c = Count.Value;
if (c == 0) { if (c == 0) {
_copyLock.EnterReadLock(); _copyLock.EnterReadLock();
} }
Count.Value = c + 1; Count.Value = c + 1;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnterExpansionProcess() public void EnterExpansionProcess()
{ {
_expansionLock.EnterWriteLock(); _expansionLock.EnterWriteLock();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnterMetaDataRead() public void EnterMetaDataRead()
{ {
_expansionLock.EnterReadLock(); _expansionLock.EnterReadLock();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExitBufferCopy() public void ExitBufferCopy()
{ {
_copyLock.ExitWriteLock(); _copyLock.ExitWriteLock();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExitBufferRead() public void ExitBufferRead()
{ {
int c = Count.Value - 1; int c = Count.Value - 1;
Count.Value = c; Count.Value = c;
if (c == 0) { if (c == 0) {
_copyLock.ExitReadLock(); _copyLock.ExitReadLock();
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExitExpansionProcess() public void ExitExpansionProcess()
{ {
_expansionLock.ExitWriteLock(); _expansionLock.ExitWriteLock();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ExitMetaDataRead() public void ExitMetaDataRead()
{ {
_expansionLock.ExitReadLock(); _expansionLock.ExitReadLock();
} }
} }
} }
+154 -154
View File
@@ -1,154 +1,154 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="CancellableTask" // file="CancellableTask"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.Threading namespace StgSharp.Threading
{ {
public class CancellableTask : IDisposable public class CancellableTask : IDisposable
{ {
private bool disposedValue; private bool disposedValue;
private CancellationTokenSource cts; private CancellationTokenSource cts;
private protected Action<CancellationToken> _startup; private protected Action<CancellationToken> _startup;
private protected Task _internalTask; private protected Task _internalTask;
private protected CancellableTask() private protected CancellableTask()
{ {
cts = new CancellationTokenSource(); cts = new CancellationTokenSource();
} }
private protected CancellableTask(Action<CancellationToken> action) private protected CancellableTask(Action<CancellationToken> action)
{ {
_internalTask = new Task(InternalAction); _internalTask = new Task(InternalAction);
cts = new CancellationTokenSource(); cts = new CancellationTokenSource();
} }
public bool IsCompleted public bool IsCompleted
{ {
get => _internalTask.IsCompleted; get => _internalTask.IsCompleted;
} }
public CancellationTokenSource Cts public CancellationTokenSource Cts
{ {
get => cts; get => cts;
} }
public int Id public int Id
{ {
get => _internalTask.Id; get => _internalTask.Id;
} }
public void Cancel() public void Cancel()
{ {
Cts.Cancel(); Cts.Cancel();
} }
public void CancelAndWait() public void CancelAndWait()
{ {
Cts.Cancel(); Cts.Cancel();
_internalTask.Wait(); _internalTask.Wait();
} }
// ~CancellableTask() // ~CancellableTask()
// { // {
// Dispose(disposing: false); // Dispose(disposing: false);
// } // }
public void Dispose() public void Dispose()
{ {
Dispose(disposing:true); Dispose(disposing:true);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
public static CancellableTask Run(Action<CancellationToken> startup) public static CancellableTask Run(Action<CancellationToken> startup)
{ {
CancellableTask ret = new CancellableTask CancellableTask ret = new CancellableTask
{ {
_startup = startup _startup = startup
}; };
ret._internalTask = Task.Run(ret.InternalAction); ret._internalTask = Task.Run(ret.InternalAction);
return ret; return ret;
} }
public void Wait() public void Wait()
{ {
_internalTask.Wait(); _internalTask.Wait();
} }
public static void WaitAll(IEnumerable<CancellableTask> tasks) public static void WaitAll(IEnumerable<CancellableTask> tasks)
{ {
IEnumerable<Task> convertedTasks = tasks.Select<CancellableTask, Task>( IEnumerable<Task> convertedTasks = tasks.Select<CancellableTask, Task>(
ct => ct._internalTask); ct => ct._internalTask);
Task.WaitAll(convertedTasks.ToArray()); Task.WaitAll(convertedTasks.ToArray());
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void InternalAction() internal void InternalAction()
{ {
_startup(Cts.Token); _startup(Cts.Token);
} }
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (!disposedValue) if (!disposedValue)
{ {
if (disposing) if (disposing)
{ {
if (IsCompleted) if (IsCompleted)
{ {
cts.Dispose(); cts.Dispose();
_internalTask.Dispose(); _internalTask.Dispose();
} else } else
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
"Cannot dispose a cancellable task before completed"); "Cannot dispose a cancellable task before completed");
} }
} }
disposedValue = true; disposedValue = true;
} }
} }
public static implicit operator Task(CancellableTask task) public static implicit operator Task(CancellableTask task)
{ {
return task._internalTask; return task._internalTask;
} }
~CancellableTask() ~CancellableTask()
{ {
Cts.Dispose(); Cts.Dispose();
} }
} }
} }
+58 -58
View File
@@ -1,58 +1,58 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="EmptyThread" // file="EmptyThread"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.Threading namespace StgSharp.Threading
{ {
public static partial class ThreadHelper public static partial class ThreadHelper
{ {
private static readonly Thread _emptyThreadCache = new Thread(() => { }); private static readonly Thread _emptyThreadCache = new Thread(() => { });
static ThreadHelper() static ThreadHelper()
{ {
_emptyThreadCache.Start(); _emptyThreadCache.Start();
} }
/// <summary> /// <summary>
/// An individual thread doing nothing, and its <see cref="Thread.IsAlive" /> <see /// An individual thread doing nothing, and its <see cref="Thread.IsAlive" /> <see
/// langword="property" /> always returns false. /// langword="property" /> always returns false.
/// </summary> /// </summary>
public static Thread EmptyThread public static Thread EmptyThread
{ {
get => _emptyThreadCache; get => _emptyThreadCache;
} }
} }
} }
+51 -51
View File
@@ -1,51 +1,51 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="MainThreadAssert" // file="MainThreadAssert"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
namespace StgSharp.Threading namespace StgSharp.Threading
{ {
public static partial class ThreadHelper public static partial class ThreadHelper
{ {
private static readonly int mainThreadId = Environment.CurrentManagedThreadId; private static readonly int mainThreadId = Environment.CurrentManagedThreadId;
private static readonly ThreadLocal<bool> isNotMainThread = new( private static readonly ThreadLocal<bool> isNotMainThread = new(
() => Environment.CurrentManagedThreadId != mainThreadId); () => Environment.CurrentManagedThreadId != mainThreadId);
[Conditional("DEBUG")] [Conditional("DEBUG")]
internal static void MainThreadAssert() internal static void MainThreadAssert()
{ {
if (isNotMainThread.Value) { if (isNotMainThread.Value) {
throw new InvalidOperationException( throw new InvalidOperationException(
$"Attempt to call sensitive operation which can be only called from main thread from thread {Environment.CurrentManagedThreadId}."); $"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" // file="NumaBuffer"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.Common.Threading namespace StgSharp.Common.Threading
{ {
public unsafe struct NumaBuffer<T> where T : unmanaged public unsafe struct NumaBuffer<T> where T : unmanaged
{ {
public readonly T* Buffers; public readonly T* Buffers;
public readonly int BufferColumnCapacity; public readonly int BufferColumnCapacity;
public readonly int BufferRowCapacity; public readonly int BufferRowCapacity;
public int CurrentColumnCount; public int CurrentColumnCount;
public int CurrentRowCount; public int CurrentRowCount;
} }
} }
+174 -174
View File
@@ -1,175 +1,175 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="SingleConsumerBell" // file="SingleConsumerBell"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Threading; using System.Threading;
namespace StgSharp.Threading namespace StgSharp.Threading
{ {
/// <summary> /// <summary>
/// A single-consumer bell/signal primitive. /// A single-consumer bell/signal primitive.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para> Producers can call <see cref="Set" /> multiple times to publish signals. The bell /// <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 /// keeps a pending count of not-yet-consumed signals. The consumer uses <see
/// cref="Wait(object, CancellationToken)" /> or <see cref="TryWait(object)" /> to consume /// 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 /// 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)" /> /// 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 /// 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 /// (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 /// avoid missed signals.</para> <para> Ownership: only the specified <c> owner </c> can call
/// the consumer APIs.</para> /// the consumer APIs.</para>
/// </remarks> /// </remarks>
public sealed class SingleConsumerBell : IDisposable public sealed class SingleConsumerBell : IDisposable
{ {
private volatile bool _disposed; private volatile bool _disposed;
private int _pending; // not volatile: used with Interlocked/Volatile APIs to avoid CS0420 private int _pending; // not volatile: used with Interlocked/Volatile APIs to avoid CS0420
private readonly ManualResetEventSlim _evt; private readonly ManualResetEventSlim _evt;
private readonly object _owner; private readonly object _owner;
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="SingleConsumerBell" />. /// Initializes a new instance of <see cref="SingleConsumerBell" />.
/// </summary> /// </summary>
/// <param name="owner"> /// <param name="owner">
/// The unique consumer identity; subsequent calls to Wait/TryWait must pass the same /// The unique consumer identity; subsequent calls to Wait/TryWait must pass the same
/// reference. /// reference.
/// </param> /// </param>
/// <param name="spinCount"> /// <param name="spinCount">
/// Spin count passed to the underlying <see cref="ManualResetEventSlim" />. /// Spin count passed to the underlying <see cref="ManualResetEventSlim" />.
/// </param> /// </param>
/// <exception cref="ArgumentNullException"> /// <exception cref="ArgumentNullException">
/// Thrown if <paramref name="owner" /> is null. /// Thrown if <paramref name="owner" /> is null.
/// </exception> /// </exception>
public SingleConsumerBell(object owner, int spinCount = 100) public SingleConsumerBell(object owner, int spinCount = 100)
{ {
_owner = owner ?? throw new ArgumentNullException(nameof(owner)); _owner = owner ?? throw new ArgumentNullException(nameof(owner));
_evt = new ManualResetEventSlim(false, spinCount); _evt = new ManualResetEventSlim(false, spinCount);
} }
/// <summary> /// <summary>
/// Disposes the bell, sets the event to wake any waiting consumer, and releases /// Disposes the bell, sets the event to wake any waiting consumer, and releases
/// resources. /// resources.
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
_disposed = true; _disposed = true;
_evt.Set(); _evt.Set();
_evt.Dispose(); _evt.Dispose();
} }
/// <summary> /// <summary>
/// Publishes a signal by incrementing the not-yet-consumed count and returns the current /// Publishes a signal by incrementing the not-yet-consumed count and returns the current
/// pending count. /// pending count.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// If the pending count transitions from 0 to 1, the event is set to wake the consumer. /// If the pending count transitions from 0 to 1, the event is set to wake the consumer.
/// </remarks> /// </remarks>
/// <returns> /// <returns>
/// The current pending count including this call. /// The current pending count including this call.
/// </returns> /// </returns>
public int Set() public int Set()
{ {
if (_disposed) { if (_disposed) {
return Volatile.Read(ref _pending); return Volatile.Read(ref _pending);
} }
// Increment pending; if we transitioned 0 -> 1, set the event to wake the consumer // Increment pending; if we transitioned 0 -> 1, set the event to wake the consumer
int newCount = Interlocked.Increment(ref _pending); int newCount = Interlocked.Increment(ref _pending);
if (newCount == 1) { if (newCount == 1) {
_evt.Set(); _evt.Set();
} }
return newCount; return newCount;
} }
/// <summary> /// <summary>
/// Attempts to consume any pending signals without blocking. /// Attempts to consume any pending signals without blocking.
/// </summary> /// </summary>
/// <param name="consumer"> /// <param name="consumer">
/// The consumer object; must be the same reference provided at construction. /// The consumer object; must be the same reference provided at construction.
/// </param> /// </param>
/// <returns> /// <returns>
/// <c> true </c> if a signal was available (pending or event set); otherwise <c> false /// <c> true </c> if a signal was available (pending or event set); otherwise <c> false
/// </c>. /// </c>.
/// </returns> /// </returns>
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// Thrown if <paramref name="consumer" /> is not the owner. /// Thrown if <paramref name="consumer" /> is not the owner.
/// </exception> /// </exception>
public bool TryWait(object consumer) public bool TryWait(object consumer)
{ {
if (!ReferenceEquals(_owner, consumer)) { if (!ReferenceEquals(_owner, consumer)) {
throw new InvalidOperationException("This bell is owned by another consumer."); throw new InvalidOperationException("This bell is owned by another consumer.");
} }
if (!_evt.IsSet && Volatile.Read(ref _pending) == 0) { if (!_evt.IsSet && Volatile.Read(ref _pending) == 0) {
return false; return false;
} }
// Aggregate consumption: clear pending and reset event // Aggregate consumption: clear pending and reset event
_ = Interlocked.Exchange(ref _pending, 0); _ = Interlocked.Exchange(ref _pending, 0);
_evt.Reset(); _evt.Reset();
// If concurrent Set happened during reset, re-set the event to avoid lost signals // If concurrent Set happened during reset, re-set the event to avoid lost signals
if (Volatile.Read(ref _pending) != 0) if (Volatile.Read(ref _pending) != 0)
{ {
_evt.Set(); _evt.Set();
} }
return true; return true;
} }
/// <summary> /// <summary>
/// Blocks until a signal is available, then consumes all pending signals. /// Blocks until a signal is available, then consumes all pending signals.
/// </summary> /// </summary>
/// <param name="consumer"> /// <param name="consumer">
/// The consumer object; must be the same reference provided at construction. /// The consumer object; must be the same reference provided at construction.
/// </param> /// </param>
/// <param name="cancellationToken"> /// <param name="cancellationToken">
/// A token to cancel the wait. /// A token to cancel the wait.
/// </param> /// </param>
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// Thrown if <paramref name="consumer" /> is not the owner. /// Thrown if <paramref name="consumer" /> is not the owner.
/// </exception> /// </exception>
public void Wait(object consumer, CancellationToken cancellationToken = default) public void Wait(object consumer, CancellationToken cancellationToken = default)
{ {
if (!ReferenceEquals(_owner, consumer)) { if (!ReferenceEquals(_owner, consumer)) {
throw new InvalidOperationException("This bell is owned by another consumer."); throw new InvalidOperationException("This bell is owned by another consumer.");
} }
_evt.Wait(cancellationToken); _evt.Wait(cancellationToken);
// Consume this round of signals: clear pending and handle reset/concurrency // Consume this round of signals: clear pending and handle reset/concurrency
_ = Interlocked.Exchange(ref _pending, 0); _ = Interlocked.Exchange(ref _pending, 0);
_evt.Reset(); _evt.Reset();
if (Volatile.Read(ref _pending) != 0) { if (Volatile.Read(ref _pending) != 0) {
_evt.Set(); _evt.Set();
} }
} }
} }
} }
@@ -1,316 +1,316 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="VariableCapacitySemaphore" // file="VariableCapacitySemaphore"
// Project: StgSharp.Common // Project: StgSharp.Common
// AuthorGroup: Nitload // AuthorGroup: Nitload
// License: MIT // License: MIT
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
namespace StgSharp.Threading namespace StgSharp.Threading
{ {
/// <summary> /// <summary>
/// A counting semaphore with adjustable maximum capacity at runtime. Implemented using one or /// A counting semaphore with adjustable maximum capacity at runtime. Implemented using one or
/// more ManualResetEventSlim (MRES) gates and atomic counters to avoid kernel semaphore /// more ManualResetEventSlim (MRES) gates and atomic counters to avoid kernel semaphore
/// overhead while supporting high-frequency Release/Wait patterns. Notes: - This is a /// overhead while supporting high-frequency Release/Wait patterns. Notes: - This is a
/// counting primitive (not a barrier). Permits accumulate up to <see cref="MaxPermits" />. - /// 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 /// Reducing <see cref="MaxPermits" /> does not revoke already issued permits; it only clamps
/// future releases. - Striped MRES can reduce contention in highly parallel waits. /// future releases. - Striped MRES can reduce contention in highly parallel waits.
/// </summary> /// </summary>
public sealed class VariableCapacitySemaphore : IDisposable public sealed class VariableCapacitySemaphore : IDisposable
{ {
// MRES stripes (one gate per stripe). All stripes are set when transitioning 0 -> >0 and reset at >0 -> 0. // MRES stripes (one gate per stripe). All stripes are set when transitioning 0 -> >0 and reset at >0 -> 0.
private readonly ManualResetEventSlim[] _events; private readonly ManualResetEventSlim[] _events;
private volatile int _current; private volatile int _current;
private volatile int _max; private volatile int _max;
private readonly int _stripeMask; // for power-of-two stripe counts; otherwise -1 private readonly int _stripeMask; // for power-of-two stripe counts; otherwise -1
/// <summary> /// <summary>
/// Create a new variable-capacity semaphore. /// Create a new variable-capacity semaphore.
/// </summary> /// </summary>
/// <param name="initialCount"> /// <param name="initialCount">
/// Initial available permits (clamped to [0, maxPermits]). /// Initial available permits (clamped to [0, maxPermits]).
/// </param> /// </param>
/// <param name="maxPermits"> /// <param name="maxPermits">
/// Logical maximum permits (>= 1). /// Logical maximum permits (>= 1).
/// </param> /// </param>
/// <param name="stripes"> /// <param name="stripes">
/// Optional stripe count for MRES (<= 0 to auto choose by CPU). /// Optional stripe count for MRES (<= 0 to auto choose by CPU).
/// </param> /// </param>
public VariableCapacitySemaphore(int initialCount = 0, int maxPermits = 1, int stripes = 0) public VariableCapacitySemaphore(int initialCount = 0, int maxPermits = 1, int stripes = 0)
{ {
ArgumentOutOfRangeException.ThrowIfLessThan(maxPermits, 1); ArgumentOutOfRangeException.ThrowIfLessThan(maxPermits, 1);
ArgumentOutOfRangeException.ThrowIfNegative(initialCount); ArgumentOutOfRangeException.ThrowIfNegative(initialCount);
_max = maxPermits; _max = maxPermits;
_current = Math.Min(initialCount, maxPermits); _current = Math.Min(initialCount, maxPermits);
int stripeCount = stripes > 0 ? stripes : ClampToPow2(Math.Min(Environment.ProcessorCount, 8)); int stripeCount = stripes > 0 ? stripes : ClampToPow2(Math.Min(Environment.ProcessorCount, 8));
_events = new ManualResetEventSlim[stripeCount]; _events = new ManualResetEventSlim[stripeCount];
bool set = _current > 0; bool set = _current > 0;
for (int i = 0; i < stripeCount; i++) { for (int i = 0; i < stripeCount; i++) {
_events[i] = new ManualResetEventSlim(set, 0); _events[i] = new ManualResetEventSlim(set, 0);
} }
_stripeMask = IsPow2(stripeCount) ? (stripeCount - 1) : -1; _stripeMask = IsPow2(stripeCount) ? (stripeCount - 1) : -1;
} }
/// <summary> /// <summary>
/// Current number of available permits. /// Current number of available permits.
/// </summary> /// </summary>
public int CurrentCount => Volatile.Read(ref _current); public int CurrentCount => Volatile.Read(ref _current);
/// <summary> /// <summary>
/// Logical maximum number of permits. Can be changed at runtime. /// Logical maximum number of permits. Can be changed at runtime.
/// </summary> /// </summary>
public int MaxPermits public int MaxPermits
{ {
get => Volatile.Read(ref _max); get => Volatile.Read(ref _max);
set set
{ {
ArgumentOutOfRangeException.ThrowIfLessThan(value, 1); ArgumentOutOfRangeException.ThrowIfLessThan(value, 1);
Volatile.Write(ref _max, value); Volatile.Write(ref _max, value);
// Clamp current to not exceed the new max (do not block; let Wait() consume down). // Clamp current to not exceed the new max (do not block; let Wait() consume down).
while (true) while (true)
{ {
int cur = Volatile.Read(ref _current); int cur = Volatile.Read(ref _current);
if (cur <= value) if (cur <= value)
{ {
break; break;
} }
if (Interlocked.CompareExchange(ref _current, value, cur) == cur) if (Interlocked.CompareExchange(ref _current, value, cur) == cur)
{ {
break; break;
} }
} }
// Reflect availability to gates // Reflect availability to gates
if (Volatile.Read(ref _current) > 0) if (Volatile.Read(ref _current) > 0)
{ {
SetAll(); SetAll();
} else } else
{ {
ResetAll(); ResetAll();
} }
} }
} }
public void Dispose() public void Dispose()
{ {
foreach (ManualResetEventSlim e in _events) { foreach (ManualResetEventSlim e in _events) {
e.Dispose(); e.Dispose();
} }
} }
/// <summary> /// <summary>
/// Release a single permit if under <see cref="MaxPermits" />. Returns the previous /// Release a single permit if under <see cref="MaxPermits" />. Returns the previous
/// count. /// count.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Release() public int Release()
{ {
while (true) while (true)
{ {
int cur = Volatile.Read(ref _current); int cur = Volatile.Read(ref _current);
int max = Volatile.Read(ref _max); int max = Volatile.Read(ref _max);
if (cur >= max) { if (cur >= max) {
return cur; return cur;
} }
if (Interlocked.CompareExchange(ref _current, cur + 1, cur) == cur) if (Interlocked.CompareExchange(ref _current, cur + 1, cur) == cur)
{ {
if (cur == 0) { if (cur == 0) {
SetAll(); SetAll();
} }
return cur; return cur;
} }
} }
} }
/// <summary> /// <summary>
/// Release multiple permits (capped by <see cref="MaxPermits" />). Returns the previous /// Release multiple permits (capped by <see cref="MaxPermits" />). Returns the previous
/// count. /// count.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Release(int permits) public int Release(int permits)
{ {
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(permits); ArgumentOutOfRangeException.ThrowIfNegativeOrZero(permits);
while (true) while (true)
{ {
int cur = Volatile.Read(ref _current); int cur = Volatile.Read(ref _current);
int max = Volatile.Read(ref _max); int max = Volatile.Read(ref _max);
if (cur >= max) { if (cur >= max) {
return cur; return cur;
} }
long target = (long)cur + permits; long target = (long)cur + permits;
if (target > max) { if (target > max) {
target = max; target = max;
} }
if (Interlocked.CompareExchange(ref _current, (int)target, cur) == cur) if (Interlocked.CompareExchange(ref _current, (int)target, cur) == cur)
{ {
if (cur == 0) { if (cur == 0) {
SetAll(); SetAll();
} }
return cur; return cur;
} }
} }
} }
/// <summary> /// <summary>
/// Try to take a permit without blocking. Returns true if acquired. /// Try to take a permit without blocking. Returns true if acquired.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryWait() public bool TryWait()
{ {
while (true) while (true)
{ {
int cur = Volatile.Read(ref _current); int cur = Volatile.Read(ref _current);
if (cur == 0) { if (cur == 0) {
return false; return false;
} }
if (Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur) if (Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
{ {
if (cur - 1 == 0) { if (cur - 1 == 0) {
ResetAll(); ResetAll();
} }
return true; return true;
} }
} }
} }
/// <summary> /// <summary>
/// Block until a permit is available, then consume one. /// Block until a permit is available, then consume one.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Wait() public void Wait()
{ {
ManualResetEventSlim gate = GetStripeEvent(); ManualResetEventSlim gate = GetStripeEvent();
SpinWait spinner = new SpinWait(); SpinWait spinner = new SpinWait();
while (true) while (true)
{ {
int cur = Volatile.Read(ref _current); int cur = Volatile.Read(ref _current);
if (cur > 0 && Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur) if (cur > 0 && Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
{ {
if (cur - 1 == 0) { if (cur - 1 == 0) {
ResetAll(); ResetAll();
} }
return; return;
} }
if (!spinner.NextSpinWillYield) if (!spinner.NextSpinWillYield)
{ {
spinner.SpinOnce(); spinner.SpinOnce();
} else } else
{ {
gate.Wait(); gate.Wait();
} }
} }
} }
/// <summary> /// <summary>
/// Block until a permit is available or canceled, then consume one. /// Block until a permit is available or canceled, then consume one.
/// </summary> /// </summary>
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken = default) public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken = default)
{ {
ManualResetEventSlim gate = GetStripeEvent(); ManualResetEventSlim gate = GetStripeEvent();
SpinWait spinner = new SpinWait(); SpinWait spinner = new SpinWait();
int start = Environment.TickCount; int start = Environment.TickCount;
int remaining = millisecondsTimeout; int remaining = millisecondsTimeout;
while (true) while (true)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
int cur = Volatile.Read(ref _current); int cur = Volatile.Read(ref _current);
if (cur > 0 && Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur) if (cur > 0 && Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
{ {
if (cur - 1 == 0) { if (cur - 1 == 0) {
ResetAll(); ResetAll();
} }
return true; return true;
} }
if (!spinner.NextSpinWillYield) if (!spinner.NextSpinWillYield)
{ {
spinner.SpinOnce(); spinner.SpinOnce();
continue; continue;
} }
if (millisecondsTimeout < 0) if (millisecondsTimeout < 0)
{ {
gate.Wait(cancellationToken); gate.Wait(cancellationToken);
continue; continue;
} }
if (remaining <= 0) { if (remaining <= 0) {
return false; return false;
} }
if (!gate.Wait(remaining, cancellationToken)) { if (!gate.Wait(remaining, cancellationToken)) {
return false; return false;
} }
remaining = millisecondsTimeout - (Environment.TickCount - start); remaining = millisecondsTimeout - (Environment.TickCount - start);
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int ClampToPow2(int x) private static int ClampToPow2(int x)
{ {
if (x < 1) { if (x < 1) {
x = 1; x = 1;
} }
if (x > 8) { if (x > 8) {
x = 8; x = 8;
} }
int p = 1; int p = 1;
while ((p << 1) <= x) { while ((p << 1) <= x) {
p <<= 1; p <<= 1;
} }
return p; return p;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private ManualResetEventSlim GetStripeEvent() private ManualResetEventSlim GetStripeEvent()
{ {
int id = Environment.CurrentManagedThreadId & 0x7fffffff; int id = Environment.CurrentManagedThreadId & 0x7fffffff;
if (_stripeMask != -1) { if (_stripeMask != -1) {
return _events[id & _stripeMask]; return _events[id & _stripeMask];
} }
int idx = id % _events.Length; int idx = id % _events.Length;
return _events[idx]; return _events[idx];
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsPow2(int x) => (x & (x - 1)) == 0; private static bool IsPow2(int x) => (x & (x - 1)) == 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ResetAll() private void ResetAll()
{ {
foreach (ManualResetEventSlim e in _events) { foreach (ManualResetEventSlim e in _events) {
e.Reset(); e.Reset();
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetAll() private void SetAll()
{ {
foreach (ManualResetEventSlim e in _events) { foreach (ManualResetEventSlim e in _events) {
e.Set(); e.Set();
} }
} }
} }
} }
+135 -135
View File
@@ -1,135 +1,135 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="StgSharpTime" // file="StgSharpTime"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using StgSharp.Collections; using StgSharp.Collections;
using StgSharp.Timing; using StgSharp.Timing;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
namespace StgSharp namespace StgSharp
{ {
public static partial class World public static partial class World
{ {
public static TimeSourceProviderBase MainTimeProvider => StgSharpTime.OnlyInstance; public static TimeSourceProviderBase MainTimeProvider => StgSharpTime.OnlyInstance;
} }
public sealed class StgSharpTime : TimeSourceProviderBase public sealed class StgSharpTime : TimeSourceProviderBase
{ {
private static readonly LinearBag<TimeSpanProvider> _subscribers = new(); private static readonly LinearBag<TimeSpanProvider> _subscribers = new();
private static readonly Stopwatch mainProvider = new(); private static readonly Stopwatch mainProvider = new();
private static Thread timeProvideThread; private static Thread timeProvideThread;
private StgSharpTime() { } private StgSharpTime() { }
// Use Stopwatch frequency as base tick frequency (ticks per second). // Use Stopwatch frequency as base tick frequency (ticks per second).
public override long Frequency => Stopwatch.Frequency; public override long Frequency => Stopwatch.Frequency;
public static StgSharpTime OnlyInstance { get; } = new StgSharpTime(); public static StgSharpTime OnlyInstance { get; } = new StgSharpTime();
public override long GetCurrentTimeSpanTick() => mainProvider.ElapsedTicks; public override long GetCurrentTimeSpanTick() => mainProvider.ElapsedTicks;
public sealed override void StartProvidingTime() public sealed override void StartProvidingTime()
{ {
if (mainProvider.IsRunning) { if (mainProvider.IsRunning) {
return; return;
} }
mainProvider.Start(); mainProvider.Start();
timeProvideThread = new Thread(ProvideTime) timeProvideThread = new Thread(ProvideTime)
{ {
IsBackground = true, IsBackground = true,
Name = "StgSharpTime" Name = "StgSharpTime"
}; };
timeProvideThread.Start(); timeProvideThread.Start();
} }
public override void StopProvidingTime() public override void StopProvidingTime()
{ {
lock (mainProvider) { lock (mainProvider) {
mainProvider.Stop(); mainProvider.Stop();
} }
} }
public void Terminate() public void Terminate()
{ {
lock (mainProvider) { lock (mainProvider) {
mainProvider.Stop(); mainProvider.Stop();
} }
} }
protected sealed override void AddSubscriberUnsynced(TimeSpanProvider subscriber) protected sealed override void AddSubscriberUnsynced(TimeSpanProvider subscriber)
{ {
_subscribers.Add(subscriber); _subscribers.Add(subscriber);
} }
protected sealed override void RemoveSubscriberUnsynced(TimeSpanProvider subscriber) protected sealed override void RemoveSubscriberUnsynced(TimeSpanProvider subscriber)
{ {
_subscribers.Remove(subscriber); _subscribers.Remove(subscriber);
} }
private void ProvideTime() private void ProvideTime()
{ {
long internalFrequency = Stopwatch.Frequency; // ticks per second long internalFrequency = Stopwatch.Frequency; // ticks per second
lock (mainProvider) { lock (mainProvider) {
mainProvider.Restart(); mainProvider.Restart();
} }
while (mainProvider.IsRunning) while (mainProvider.IsRunning)
{ {
long elapsedTicks; long elapsedTicks;
lock (mainProvider) { lock (mainProvider) {
elapsedTicks = mainProvider.ElapsedTicks; elapsedTicks = mainProvider.ElapsedTicks;
} }
if (_subscribers.Count == 0) if (_subscribers.Count == 0)
{ {
Thread.SpinWait(50); Thread.SpinWait(50);
continue; continue;
} }
lock (SubscribeLock) lock (SubscribeLock)
{ {
for (int i = 0; i < _subscribers.Count; i++) for (int i = 0; i < _subscribers.Count; i++)
{ {
if (!_subscribers[i].OnSpanTick(elapsedTicks)) if (!_subscribers[i].OnSpanTick(elapsedTicks))
{ {
// Console.WriteLine("removed"); // Console.WriteLine("removed");
_subscribers.RemoveAt(i); _subscribers.RemoveAt(i);
i--; // adjust index after removal i--; // adjust index after removal
} }
} }
} }
// Optional: brief spin to avoid tight loop if frame lengths are large. // Optional: brief spin to avoid tight loop if frame lengths are large.
Thread.SpinWait(20); Thread.SpinWait(20);
} }
} }
} }
} }
+309 -309
View File
@@ -1,309 +1,309 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="TimeSequence" // file="TimeSequence"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using StgSharp.Timing; using StgSharp.Timing;
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace StgSharp.Common.Timing namespace StgSharp.Common.Timing
{ {
public abstract class TimeSequence public abstract class TimeSequence
{ {
public long BeginningTick { get; protected set; } public long BeginningTick { get; protected set; }
public long EndingTick { get; protected set; } public long EndingTick { get; protected set; }
public long PreviousFrameTick { get; protected set; } public long PreviousFrameTick { get; protected set; }
public static TimeSequence CreateLinear( public static TimeSequence CreateLinear(
double totalSeconds, double totalSeconds,
double frameSeconds double frameSeconds
) )
{ {
if (double.IsPositiveInfinity(totalSeconds)) { if (double.IsPositiveInfinity(totalSeconds)) {
return new InfiniteLinearTimeSequence(frameSeconds); return new InfiniteLinearTimeSequence(frameSeconds);
} }
return new LinearTimeSequence(totalSeconds, frameSeconds); return new LinearTimeSequence(totalSeconds, frameSeconds);
} }
public static TimeSequence CreateLinearInfinite( public static TimeSequence CreateLinearInfinite(
double frameSeconds double frameSeconds
) )
{ {
return new InfiniteLinearTimeSequence(frameSeconds); return new InfiniteLinearTimeSequence(frameSeconds);
} }
public static TimeSequence CreateLogarithmic( public static TimeSequence CreateLogarithmic(
double initialSliceSeconds, double initialSliceSeconds,
double totalSeconds, double totalSeconds,
double growthMultiplier, double growthMultiplier,
double minSliceSeconds double minSliceSeconds
) => new LogTimeSequence(initialSliceSeconds, totalSeconds, growthMultiplier, ) => new LogTimeSequence(initialSliceSeconds, totalSeconds, growthMultiplier,
minSliceSeconds); minSliceSeconds);
public abstract bool EndsAt( public abstract bool EndsAt(
long currentTick long currentTick
); );
public abstract bool IsNextFrameReady( public abstract bool IsNextFrameReady(
long currentTick long currentTick
); );
public abstract void StartFrom( public abstract void StartFrom(
TimeSourceProviderBase source TimeSourceProviderBase source
); );
} }
internal sealed class LinearTimeSequence : TimeSequence internal sealed class LinearTimeSequence : TimeSequence
{ {
private readonly double _frameSeconds; private readonly double _frameSeconds;
private readonly double _totalSeconds; private readonly double _totalSeconds;
private long _frameLengthTicks; private long _frameLengthTicks;
public LinearTimeSequence( public LinearTimeSequence(
double totalSeconds, double totalSeconds,
double frameSeconds double frameSeconds
) )
{ {
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(totalSeconds); ArgumentOutOfRangeException.ThrowIfNegativeOrZero(totalSeconds);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(frameSeconds); ArgumentOutOfRangeException.ThrowIfNegativeOrZero(frameSeconds);
if (frameSeconds > totalSeconds) { if (frameSeconds > totalSeconds) {
throw new ArgumentException("Frame length greater than total length."); throw new ArgumentException("Frame length greater than total length.");
} }
_totalSeconds = totalSeconds; _totalSeconds = totalSeconds;
_frameSeconds = frameSeconds; _frameSeconds = frameSeconds;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool EndsAt( public override bool EndsAt(
long currentTick long currentTick
) => currentTick >= EndingTick; ) => currentTick >= EndingTick;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool IsNextFrameReady( public override bool IsNextFrameReady(
long currentTick long currentTick
) )
{ {
long span = currentTick - PreviousFrameTick; long span = currentTick - PreviousFrameTick;
if (span >= _frameLengthTicks && currentTick < EndingTick) if (span >= _frameLengthTicks && currentTick < EndingTick)
{ {
PreviousFrameTick += _frameLengthTicks; PreviousFrameTick += _frameLengthTicks;
return true; return true;
} }
return false; return false;
} }
public override void StartFrom( public override void StartFrom(
TimeSourceProviderBase source TimeSourceProviderBase source
) )
{ {
long f = source?.Frequency ?? throw new ArgumentNullException(nameof(source)); long f = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
_frameLengthTicks = (long)Math.Max(1, _frameSeconds * f); _frameLengthTicks = (long)Math.Max(1, _frameSeconds * f);
long totalTicks = (long)Math.Max(_frameLengthTicks, Math.Round(_totalSeconds * f, MidpointRounding.AwayFromZero)); long totalTicks = (long)Math.Max(_frameLengthTicks, Math.Round(_totalSeconds * f, MidpointRounding.AwayFromZero));
BeginningTick = source.GetCurrentTimeSpanTick(); BeginningTick = source.GetCurrentTimeSpanTick();
EndingTick = BeginningTick + totalTicks; EndingTick = BeginningTick + totalTicks;
EndingTick = EndingTick < 0 ? long.MaxValue : EndingTick; EndingTick = EndingTick < 0 ? long.MaxValue : EndingTick;
PreviousFrameTick = BeginningTick; PreviousFrameTick = BeginningTick;
} }
} }
/// <summary> /// <summary>
/// Logarithmic time sequence with growth factor and minimum slice length enforcement. /// Logarithmic time sequence with growth factor and minimum slice length enforcement.
/// </summary> /// </summary>
internal sealed class LogTimeSequence : TimeSequence internal sealed class LogTimeSequence : TimeSequence
{ {
private bool _finalEmitted; private bool _finalEmitted;
private readonly double _initialSeconds; private readonly double _initialSeconds;
private double _lastBoundarySecondsAdjusted; private double _lastBoundarySecondsAdjusted;
private readonly double _minSliceSeconds; private readonly double _minSliceSeconds;
private readonly double _multiplier; private readonly double _multiplier;
private double _nextBoundarySecondsAdjusted; private double _nextBoundarySecondsAdjusted;
private readonly double _totalSeconds; private readonly double _totalSeconds;
private int _sliceIndex; private int _sliceIndex;
private long _freq; private long _freq;
private long _nextBoundaryTick; private long _nextBoundaryTick;
private long _totalTicks; private long _totalTicks;
public LogTimeSequence( public LogTimeSequence(
double initialSliceSeconds, double initialSliceSeconds,
double totalSeconds, double totalSeconds,
double growthMultiplier, double growthMultiplier,
double minSliceSeconds double minSliceSeconds
) )
{ {
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(initialSliceSeconds); ArgumentOutOfRangeException.ThrowIfNegativeOrZero(initialSliceSeconds);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(totalSeconds); ArgumentOutOfRangeException.ThrowIfNegativeOrZero(totalSeconds);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(growthMultiplier); ArgumentOutOfRangeException.ThrowIfNegativeOrZero(growthMultiplier);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(minSliceSeconds); ArgumentOutOfRangeException.ThrowIfNegativeOrZero(minSliceSeconds);
if (growthMultiplier < 1.0) { if (growthMultiplier < 1.0) {
throw new ArgumentException("growthMultiplier must be >= 1"); throw new ArgumentException("growthMultiplier must be >= 1");
} }
if (initialSliceSeconds > totalSeconds) { if (initialSliceSeconds > totalSeconds) {
throw new ArgumentException("Initial slice length greater than total length."); throw new ArgumentException("Initial slice length greater than total length.");
} }
_initialSeconds = initialSliceSeconds; _initialSeconds = initialSliceSeconds;
_totalSeconds = totalSeconds; _totalSeconds = totalSeconds;
_multiplier = growthMultiplier; _multiplier = growthMultiplier;
_minSliceSeconds = minSliceSeconds; _minSliceSeconds = minSliceSeconds;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool EndsAt( public override bool EndsAt(
long currentTick long currentTick
) )
{ {
return currentTick >= EndingTick; return currentTick >= EndingTick;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool IsNextFrameReady( public override bool IsNextFrameReady(
long currentTick long currentTick
) )
{ {
// Emit final once when reaching or passing end. // Emit final once when reaching or passing end.
if (currentTick >= EndingTick) if (currentTick >= EndingTick)
{ {
if (_finalEmitted) { if (_finalEmitted) {
return false; return false;
} }
PreviousFrameTick = EndingTick; PreviousFrameTick = EndingTick;
_finalEmitted = true; _finalEmitted = true;
return true; return true;
} }
// Not reached end: only when we cross the next boundary tick do we emit. // Not reached end: only when we cross the next boundary tick do we emit.
if (currentTick < _nextBoundaryTick) if (currentTick < _nextBoundaryTick)
{ {
return false; return false;
} }
// Advance to this boundary. // Advance to this boundary.
PreviousFrameTick = _nextBoundaryTick; PreviousFrameTick = _nextBoundaryTick;
_lastBoundarySecondsAdjusted = _nextBoundarySecondsAdjusted; _lastBoundarySecondsAdjusted = _nextBoundarySecondsAdjusted;
_sliceIndex++; _sliceIndex++;
// Compute next cumulative boundary seconds with multiplier, then enforce min-slice and total cap. // Compute next cumulative boundary seconds with multiplier, then enforce min-slice and total cap.
double next = _initialSeconds * Math.Pow(_multiplier, _sliceIndex); double next = _initialSeconds * Math.Pow(_multiplier, _sliceIndex);
if (next > _totalSeconds) { if (next > _totalSeconds) {
next = _totalSeconds; next = _totalSeconds;
} }
double delta = next - _lastBoundarySecondsAdjusted; double delta = next - _lastBoundarySecondsAdjusted;
if (delta < _minSliceSeconds) if (delta < _minSliceSeconds)
{ {
next = _lastBoundarySecondsAdjusted + _minSliceSeconds; next = _lastBoundarySecondsAdjusted + _minSliceSeconds;
if (next > _totalSeconds) { if (next > _totalSeconds) {
next = _totalSeconds; next = _totalSeconds;
} }
} }
_nextBoundarySecondsAdjusted = next; _nextBoundarySecondsAdjusted = next;
_nextBoundaryTick = (_nextBoundarySecondsAdjusted >= _totalSeconds) ? _nextBoundaryTick = (_nextBoundarySecondsAdjusted >= _totalSeconds) ?
EndingTick : EndingTick :
BeginningTick + SecondsToTicks(_nextBoundarySecondsAdjusted); BeginningTick + SecondsToTicks(_nextBoundarySecondsAdjusted);
return true; return true;
} }
public override void StartFrom( public override void StartFrom(
TimeSourceProviderBase source TimeSourceProviderBase source
) )
{ {
_freq = source?.Frequency ?? throw new ArgumentNullException(nameof(source)); _freq = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
BeginningTick = source.GetCurrentTimeSpanTick(); BeginningTick = source.GetCurrentTimeSpanTick();
_totalTicks = SecondsToTicks(_totalSeconds); _totalTicks = SecondsToTicks(_totalSeconds);
EndingTick = BeginningTick + _totalTicks; EndingTick = BeginningTick + _totalTicks;
_sliceIndex = 0; _sliceIndex = 0;
_lastBoundarySecondsAdjusted = 0.0; _lastBoundarySecondsAdjusted = 0.0;
_nextBoundarySecondsAdjusted = _initialSeconds; _nextBoundarySecondsAdjusted = _initialSeconds;
_nextBoundaryTick = BeginningTick + SecondsToTicks(_nextBoundarySecondsAdjusted); _nextBoundaryTick = BeginningTick + SecondsToTicks(_nextBoundarySecondsAdjusted);
PreviousFrameTick = BeginningTick; PreviousFrameTick = BeginningTick;
_finalEmitted = false; _finalEmitted = false;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private long SecondsToTicks( private long SecondsToTicks(
double seconds double seconds
) => (long)Math.Max(1L, Math.Round(seconds * _freq, MidpointRounding.AwayFromZero)); ) => (long)Math.Max(1L, Math.Round(seconds * _freq, MidpointRounding.AwayFromZero));
} }
/// <summary> /// <summary>
/// Infinite linear time sequence: emits frames at fixed interval and never ends. /// Infinite linear time sequence: emits frames at fixed interval and never ends.
/// </summary> /// </summary>
internal sealed class InfiniteLinearTimeSequence : TimeSequence internal sealed class InfiniteLinearTimeSequence : TimeSequence
{ {
private readonly double _frameSeconds; private readonly double _frameSeconds;
private long _frameLengthTicks; private long _frameLengthTicks;
public InfiniteLinearTimeSequence( public InfiniteLinearTimeSequence(
double frameSeconds double frameSeconds
) )
{ {
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(frameSeconds); ArgumentOutOfRangeException.ThrowIfNegativeOrZero(frameSeconds);
_frameSeconds = frameSeconds; _frameSeconds = frameSeconds;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool EndsAt( public override bool EndsAt(
long currentTick long currentTick
) => false; // never ends ) => false; // never ends
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool IsNextFrameReady( public override bool IsNextFrameReady(
long currentTick long currentTick
) )
{ {
long span = currentTick - PreviousFrameTick; long span = currentTick - PreviousFrameTick;
if (span >= _frameLengthTicks) if (span >= _frameLengthTicks)
{ {
PreviousFrameTick += _frameLengthTicks; PreviousFrameTick += _frameLengthTicks;
return true; return true;
} }
return false; return false;
} }
public override void StartFrom( public override void StartFrom(
TimeSourceProviderBase source TimeSourceProviderBase source
) )
{ {
long f = source?.Frequency ?? throw new ArgumentNullException(nameof(source)); long f = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
_frameLengthTicks = (long)Math.Max(1, _frameSeconds * f); _frameLengthTicks = (long)Math.Max(1, _frameSeconds * f);
BeginningTick = source.GetCurrentTimeSpanTick(); BeginningTick = source.GetCurrentTimeSpanTick();
EndingTick = long.MaxValue; // sentinel EndingTick = long.MaxValue; // sentinel
PreviousFrameTick = BeginningTick; PreviousFrameTick = BeginningTick;
} }
} }
} }
+68 -68
View File
@@ -1,68 +1,68 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="TimeSourceProvider" // file="TimeSourceProvider"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using StgSharp; using StgSharp;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
namespace StgSharp.Timing namespace StgSharp.Timing
{ {
public abstract class TimeSourceProviderBase public abstract class TimeSourceProviderBase
{ {
public abstract long Frequency { get; } public abstract long Frequency { get; }
protected object SubscribeLock { get; } = new(); protected object SubscribeLock { get; } = new();
public void AddSubscriber(TimeSpanProvider subscriber) public void AddSubscriber(TimeSpanProvider subscriber)
{ {
lock (SubscribeLock) { lock (SubscribeLock) {
AddSubscriberUnsynced(subscriber); AddSubscriberUnsynced(subscriber);
} }
} }
public abstract long GetCurrentTimeSpanTick(); public abstract long GetCurrentTimeSpanTick();
public void RemoveSubscriber(TimeSpanProvider subscriber) public void RemoveSubscriber(TimeSpanProvider subscriber)
{ {
lock (SubscribeLock) { lock (SubscribeLock) {
RemoveSubscriberUnsynced(subscriber); RemoveSubscriberUnsynced(subscriber);
} }
} }
public abstract void StartProvidingTime(); public abstract void StartProvidingTime();
public abstract void StopProvidingTime(); public abstract void StopProvidingTime();
protected abstract void AddSubscriberUnsynced(TimeSpanProvider subscriber); protected abstract void AddSubscriberUnsynced(TimeSpanProvider subscriber);
protected abstract void RemoveSubscriberUnsynced(TimeSpanProvider subscriber); protected abstract void RemoveSubscriberUnsynced(TimeSpanProvider subscriber);
} }
} }
@@ -1,234 +1,234 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="TimeSpanProvider.Awaiting" // file="TimeSpanProvider.Awaiting"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload Space // AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved. // Copyright (c) Nitload Space. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using StgSharp.Mathematics.Memory; using StgSharp.Mathematics.Memory;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace StgSharp.Timing namespace StgSharp.Timing
{ {
public partial class TimeSpanProvider public partial class TimeSpanProvider
{ {
private static readonly ConcurrentBufferStack<int> s_unusedID = new ConcurrentBufferStack<int>(8); private static readonly ConcurrentBufferStack<int> s_unusedID = new ConcurrentBufferStack<int>(8);
private static int s_maxID; private static int s_maxID;
private EventHandler? _refreshHandler, _missHandler; private EventHandler? _refreshHandler, _missHandler;
private int _size, _id; private int _size, _id;
private long _pendingSpans; // Number of unconsumed span ticks since last Wait. private long _pendingSpans; // Number of unconsumed span ticks since last Wait.
private readonly ManualResetEventSlim _event = new(); private readonly ManualResetEventSlim _event = new();
private readonly object _participantObject = new(); private readonly object _participantObject = new();
public event EventHandler MissTimeSpanRefreshed public event EventHandler MissTimeSpanRefreshed
{ {
add => _missHandler += value; add => _missHandler += value;
remove => _missHandler -= value; remove => _missHandler -= value;
} }
public event EventHandler TimeSpanRefreshed public event EventHandler TimeSpanRefreshed
{ {
add => _refreshHandler += value; add => _refreshHandler += value;
remove => _refreshHandler -= value; remove => _refreshHandler -= value;
} }
/// <summary> /// <summary>
/// True if there is at least one unconsumed span tick (PendingSpanCount &gt; 0). /// True if there is at least one unconsumed span tick (PendingSpanCount &gt; 0).
/// </summary> /// </summary>
public bool HasPendingSpan => PendingSpanCount > 0; public bool HasPendingSpan => PendingSpanCount > 0;
public int TokenID public int TokenID
{ {
get => _id; get => _id;
private set => _id = value; private set => _id = value;
} }
/// <summary> /// <summary>
/// Number of span ticks not yet consumed by Wait ( &gt; 0 means last tick not waited). /// Number of span ticks not yet consumed by Wait ( &gt; 0 means last tick not waited).
/// </summary> /// </summary>
public long PendingSpanCount => Volatile.Read(ref _pendingSpans); public long PendingSpanCount => Volatile.Read(ref _pendingSpans);
internal ManualResetEventSlim AwaitingEvent internal ManualResetEventSlim AwaitingEvent
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _event; get => _event;
} }
public void Dispose() public void Dispose()
{ {
// mark provider as ended so external users can observe terminal state // mark provider as ended so external users can observe terminal state
Volatile.Write(ref _endedFlag, 1); Volatile.Write(ref _endedFlag, 1);
s_unusedID.Push(TokenID); s_unusedID.Push(TokenID);
_event.Set(); _event.Set();
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
public override int GetHashCode() => _id; public override int GetHashCode() => _id;
public void Participant() public void Participant()
{ {
lock (_participantObject) lock (_participantObject)
{ {
// Increase the maximum concurrent consumers for the next tick. // Increase the maximum concurrent consumers for the next tick.
_size++; _size++;
} }
} }
public void WaitNextSpan() public void WaitNextSpan()
{ {
_event.Wait(); _event.Wait();
// Consume one span slot (clamp at 0 so multiple subscribers won't make it negative). // Consume one span slot (clamp at 0 so multiple subscribers won't make it negative).
while (true) while (true)
{ {
long cur = Volatile.Read(ref _pendingSpans); long cur = Volatile.Read(ref _pendingSpans);
if (cur == 0) { if (cur == 0) {
return; return;
} }
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur) if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
{ {
if (cur - 1 == 0) if (cur - 1 == 0)
{ {
// Last consumer resets the gate for the next tick. // Last consumer resets the gate for the next tick.
_event.Reset(); _event.Reset();
} }
return; return;
} }
} }
} }
public async Task WaitNextSpanAsync() public async Task WaitNextSpanAsync()
{ {
await Task.Run(() => _event.Wait()).ConfigureAwait(false); await Task.Run(() => _event.Wait()).ConfigureAwait(false);
while (true) while (true)
{ {
long cur = Volatile.Read(ref _pendingSpans); long cur = Volatile.Read(ref _pendingSpans);
if (cur == 0) if (cur == 0)
{ {
break; break;
} }
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur) if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
{ {
if (cur - 1 == 0) { if (cur - 1 == 0) {
_event.Reset(); _event.Reset();
} }
break; break;
} }
} }
} }
/// <summary> /// <summary>
/// Wait for the next timespan tick, but honor the provided cancellation token. Throws /// Wait for the next timespan tick, but honor the provided cancellation token. Throws
/// OperationCanceledException when token is cancelled. If the provider is disposed during /// OperationCanceledException when token is cancelled. If the provider is disposed during
/// wait an OperationCanceledException is thrown to indicate end. /// wait an OperationCanceledException is thrown to indicate end.
/// </summary> /// </summary>
public Task WaitNextSpanAsync(CancellationToken ct) public Task WaitNextSpanAsync(CancellationToken ct)
{ {
// Block the current thread until the event is signaled or cancellation requested. // Block the current thread until the event is signaled or cancellation requested.
_event.Wait(ct); _event.Wait(ct);
// consume one pending span (same logic as non-cancellable variant) // consume one pending span (same logic as non-cancellable variant)
while (true) while (true)
{ {
long cur = Volatile.Read(ref _pendingSpans); long cur = Volatile.Read(ref _pendingSpans);
if (cur == 0) if (cur == 0)
{ {
break; break;
} }
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur) if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
{ {
if (cur - 1 == 0) { if (cur - 1 == 0) {
_event.Reset(); _event.Reset();
} }
break; break;
} }
} }
return Task.CompletedTask; return Task.CompletedTask;
} }
internal void MissRefresh() => _missHandler?.Invoke(this, EventArgs.Empty); // Legacy compatibility. internal void MissRefresh() => _missHandler?.Invoke(this, EventArgs.Empty); // Legacy compatibility.
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="currentTick"> /// <param name="currentTick">
/// Tick passed since binded time source. /// Tick passed since binded time source.
/// </param> /// </param>
/// <returns> /// <returns>
/// True if the span provider is working. False if time is over (the sequence ended). /// True if the span provider is working. False if time is over (the sequence ended).
/// </returns> /// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool OnSpanTick(long currentTick) internal bool OnSpanTick(long currentTick)
{ {
if (_sequence.EndsAt(currentTick)) if (_sequence.EndsAt(currentTick))
{ {
// mark ended due to sequence timeout, then dispose // mark ended due to sequence timeout, then dispose
Volatile.Write(ref _endedFlag, 1); Volatile.Write(ref _endedFlag, 1);
// Console.WriteLine("Time span ended."); // Console.WriteLine("Time span ended.");
Dispose(); Dispose();
return false; return false;
} }
if (!_sequence.IsNextFrameReady(currentTick)) { if (!_sequence.IsNextFrameReady(currentTick)) {
return true; return true;
} }
// Console.WriteLine("Frame hit!"); // Console.WriteLine("Frame hit!");
// Fast path: if no pending consumers, initialize the counter to current capacity (_size). // Fast path: if no pending consumers, initialize the counter to current capacity (_size).
if (Volatile.Read(ref _pendingSpans) == 0) if (Volatile.Read(ref _pendingSpans) == 0)
{ {
int toIssue = Math.Max(1, Volatile.Read(ref _size)); int toIssue = Math.Max(1, Volatile.Read(ref _size));
if (Interlocked.CompareExchange(ref _pendingSpans, toIssue, 0) == 0) if (Interlocked.CompareExchange(ref _pendingSpans, toIssue, 0) == 0)
{ {
// Open the gate for all current waiters (up to `_size`). // Open the gate for all current waiters (up to `_size`).
_event.Set(); _event.Set();
_refreshHandler?.Invoke(this, EventArgs.Empty); _refreshHandler?.Invoke(this, EventArgs.Empty);
} }
} else } else
{ {
// A tick is already pending (or was just set by another thread): mark as miss. // A tick is already pending (or was just set by another thread): mark as miss.
_missHandler?.Invoke(this, EventArgs.Empty); _missHandler?.Invoke(this, EventArgs.Empty);
} }
return true; return true;
} }
~TimeSpanProvider() => Dispose(); ~TimeSpanProvider() => Dispose();
} }
} }
+93 -93
View File
@@ -1,94 +1,94 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="TimeSpanProvider" // file="TimeSpanProvider"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using StgSharp.Common.Timing; using StgSharp.Common.Timing;
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
namespace StgSharp.Timing namespace StgSharp.Timing
{ {
/// <summary> /// <summary>
/// TimeSpanProvider: uses a bound TimeSequence and TimeSourceProviderBase to publish span /// TimeSpanProvider: uses a bound TimeSequence and TimeSourceProviderBase to publish span
/// ticks. Each OnSpanTick advances the sequence; when sequence ends provider unsubscribes. /// ticks. Each OnSpanTick advances the sequence; when sequence ends provider unsubscribes.
/// </summary> /// </summary>
public partial class TimeSpanProvider public partial class TimeSpanProvider
{ {
// private bool _subscribed; // private bool _subscribed;
private int _emittedSpanCount; // legacy counter private int _emittedSpanCount; // legacy counter
// internal ended flag (0 = running, 1 = ended) // internal ended flag (0 = running, 1 = ended)
private int _endedFlag; private int _endedFlag;
private readonly TimeSequence _sequence; private readonly TimeSequence _sequence;
private readonly TimeSourceProviderBase _timeSource; private readonly TimeSourceProviderBase _timeSource;
public TimeSpanProvider( public TimeSpanProvider(
TimeSequence sequence, TimeSequence sequence,
TimeSourceProviderBase provider, TimeSourceProviderBase provider,
int maxWaitCount = 1) int maxWaitCount = 1)
{ {
ArgumentNullException.ThrowIfNull(provider); ArgumentNullException.ThrowIfNull(provider);
ArgumentNullException.ThrowIfNull(sequence); ArgumentNullException.ThrowIfNull(sequence);
_timeSource = provider; _timeSource = provider;
_sequence = sequence; _sequence = sequence;
_sequence.StartFrom(provider); _sequence.StartFrom(provider);
provider.AddSubscriber(this); provider.AddSubscriber(this);
// _subscribed = true; // _subscribed = true;
if (!s_unusedID.TryPop(out int id)) if (!s_unusedID.TryPop(out int id))
{ {
id = Interlocked.Increment(ref s_maxID); id = Interlocked.Increment(ref s_maxID);
} }
TokenID = id; TokenID = id;
_size = maxWaitCount > 0 ? maxWaitCount : 1; _size = maxWaitCount > 0 ? maxWaitCount : 1;
_event = new ManualResetEventSlim(initialState:false, spinCount:0); _event = new ManualResetEventSlim(initialState:false, spinCount:0);
_pendingSpans = 0; _pendingSpans = 0;
} }
/// <summary> /// <summary>
/// True when the provider has reached its end and will not emit further spans. /// True when the provider has reached its end and will not emit further spans.
/// </summary> /// </summary>
public bool IsEnded => Volatile.Read(ref _endedFlag) != 0; public bool IsEnded => Volatile.Read(ref _endedFlag) != 0;
/// <summary> /// <summary>
/// Current emitted time in seconds relative to the sequence beginning. /// Current emitted time in seconds relative to the sequence beginning.
/// </summary> /// </summary>
public double CurrentSecond public double CurrentSecond
{ {
get => (double)(_sequence.PreviousFrameTick - _sequence.BeginningTick) / _timeSource.Frequency; get => (double)(_sequence.PreviousFrameTick - _sequence.BeginningTick) / _timeSource.Frequency;
} }
public int CurrentSpan => _emittedSpanCount; public int CurrentSpan => _emittedSpanCount;
public static TimeSourceProviderBase DefaultProvider => World.MainTimeProvider; public static TimeSourceProviderBase DefaultProvider => World.MainTimeProvider;
public static explicit operator int(TimeSpanProvider provider) => provider?._emittedSpanCount ?? public static explicit operator int(TimeSpanProvider provider) => provider?._emittedSpanCount ??
0; 0;
} }
} }
+8 -8
View File
@@ -1,8 +1,8 @@
namespace EM.LowLevel namespace EM.LowLevel
{ {
/// <summary> /// <summary>
/// Marker interface for command types that can be built into bytes by an ICommandBuilder. /// Marker interface for command types that can be built into bytes by an ICommandBuilder.
/// Implement this on record/struct command shapes. /// Implement this on record/struct command shapes.
/// </summary> /// </summary>
public interface ICommand { } public interface ICommand { }
} }
+30 -30
View File
@@ -1,30 +1,30 @@
namespace EM.LowLevel namespace EM.LowLevel
{ {
/// <summary> /// <summary>
/// Builds device/peripheral commands from C#-friendly types (enums, records, structs, etc.). /// Builds device/peripheral commands from C#-friendly types (enums, records, structs, etc.).
/// </summary> /// </summary>
public interface ICommandBuilder public interface ICommandBuilder
{ {
/// <summary> /// <summary>
/// Build a command payload (already framed or raw depending on implementation contract) from a /// Build a command payload (already framed or raw depending on implementation contract) from a
/// strongly-typed command description. /// strongly-typed command description.
/// </summary> /// </summary>
/// <typeparam name="TCommand"> /// <typeparam name="TCommand">
/// Command type (e.g., an enum or a record/struct) /// Command type (e.g., an enum or a record/struct)
/// </typeparam> /// </typeparam>
/// <param name="command"> /// <param name="command">
/// Command value to build /// Command value to build
/// </param> /// </param>
/// <returns> /// <returns>
/// Command bytes /// Command bytes
/// </returns> /// </returns>
ReadOnlyMemory<byte> Build<TCommand>(TCommand command) where TCommand: notnull; ReadOnlyMemory<byte> Build<TCommand>(TCommand command) where TCommand: notnull;
/// <summary> /// <summary>
/// Try-build variant to avoid throwing and allow buffer reuse strategies. /// Try-build variant to avoid throwing and allow buffer reuse strategies.
/// </summary> /// </summary>
bool TryBuild<TCommand>(TCommand command, out ReadOnlyMemory<byte> bytes) where TCommand: notnull; bool TryBuild<TCommand>(TCommand command, out ReadOnlyMemory<byte> bytes) where TCommand: notnull;
} }
} }
+29 -29
View File
@@ -1,29 +1,29 @@
namespace EM.LowLevel namespace EM.LowLevel
{ {
/// <summary> /// <summary>
/// Decodes device-specific binary responses into C#-friendly models. /// Decodes device-specific binary responses into C#-friendly models.
/// </summary> /// </summary>
public interface IDataDecoder public interface IDataDecoder
{ {
/// <summary> /// <summary>
/// Decode a response of a known type into a model. /// Decode a response of a known type into a model.
/// </summary> /// </summary>
/// <typeparam name="TModel"> /// <typeparam name="TModel">
/// Target model type /// Target model type
/// </typeparam> /// </typeparam>
/// <param name="payload"> /// <param name="payload">
/// Raw or deframed payload depending on transport contract /// Raw or deframed payload depending on transport contract
/// </param> /// </param>
/// <returns> /// <returns>
/// Decoded model /// Decoded model
/// </returns> /// </returns>
TModel Decode<TModel>(ReadOnlySpan<byte> payload); TModel Decode<TModel>(ReadOnlySpan<byte> payload);
/// <summary> /// <summary>
/// Try-decode variant to avoid throwing on partial/bad frames. /// Try-decode variant to avoid throwing on partial/bad frames.
/// </summary> /// </summary>
bool TryDecode<TModel>(ReadOnlySpan<byte> payload, out TModel model); bool TryDecode<TModel>(ReadOnlySpan<byte> payload, out TModel model);
} }
} }
+54 -54
View File
@@ -1,54 +1,54 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace EM.LowLevel namespace EM.LowLevel
{ {
/// <summary> /// <summary>
/// Generic high-level controller that composes a PortStream, an ICommandBuilder and an /// Generic high-level controller that composes a PortStream, an ICommandBuilder and an
/// IDataDecoder to provide a complete peripheral control workflow. /// IDataDecoder to provide a complete peripheral control workflow.
/// </summary> /// </summary>
/// <typeparam name="TPort"> /// <typeparam name="TPort">
/// Concrete transport deriving from PortStream /// Concrete transport deriving from PortStream
/// </typeparam> /// </typeparam>
/// <typeparam name="TBuilder"> /// <typeparam name="TBuilder">
/// Concrete command builder /// Concrete command builder
/// </typeparam> /// </typeparam>
/// <typeparam name="TDecoder"> /// <typeparam name="TDecoder">
/// Concrete data decoder /// Concrete data decoder
/// </typeparam> /// </typeparam>
public interface IPortController<TPort, TBuilder, TDecoder> where TPort: PortStream where TBuilder: ICommandBuilder public interface IPortController<TPort, TBuilder, TDecoder> where TPort: PortStream where TBuilder: ICommandBuilder
where TDecoder: IDataDecoder where TDecoder: IDataDecoder
{ {
TBuilder CommandBuilder { get; } TBuilder CommandBuilder { get; }
TDecoder Decoder { get; } TDecoder Decoder { get; }
TPort Port { get; } TPort Port { get; }
/// <summary> /// <summary>
/// Close the underlying port. /// Close the underlying port.
/// </summary> /// </summary>
void Close(); void Close();
/// <summary> /// <summary>
/// Ensure the underlying port is opened and ready (delegates to Port.OpenAsync). /// Ensure the underlying port is opened and ready (delegates to Port.OpenAsync).
/// </summary> /// </summary>
ValueTask OpenAsync(CancellationToken cancellationToken = default); ValueTask OpenAsync(CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Build a command, send it via the port and decode the response into a model. /// Build a command, send it via the port and decode the response into a model.
/// </summary> /// </summary>
ValueTask<TResponse> RoundTripAsync<TCommand, TResponse>( ValueTask<TResponse> RoundTripAsync<TCommand, TResponse>(
TCommand command, TCommand command,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
where TCommand: notnull; where TCommand: notnull;
/// <summary> /// <summary>
/// Build a command and send it without expecting a typed response. /// Build a command and send it without expecting a typed response.
/// </summary> /// </summary>
ValueTask WriteAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default) ValueTask WriteAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default)
where TCommand: notnull; where TCommand: notnull;
} }
} }
+7 -7
View File
@@ -1,7 +1,7 @@
namespace EM.LowLevel namespace EM.LowLevel
{ {
/// <summary> /// <summary>
/// Marker interface for decoded response models returned by IDataDecoder. /// Marker interface for decoded response models returned by IDataDecoder.
/// </summary> /// </summary>
public interface IResponse { } public interface IResponse { }
} }
+35 -35
View File
@@ -1,35 +1,35 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace EM.LowLevel namespace EM.LowLevel
{ {
/// <summary> /// <summary>
/// High-level transport abstraction for device communication. Derives from Stream and /// High-level transport abstraction for device communication. Derives from Stream and
/// encapsulates initialization/handshake and frame (header/tail/checksum) handling as needed /// encapsulates initialization/handshake and frame (header/tail/checksum) handling as needed
/// by the device. Implementations may wrap SerialPort, Sockets, USB, etc. /// by the device. Implementations may wrap SerialPort, Sockets, USB, etc.
/// </summary> /// </summary>
public abstract class PortStream : Stream public abstract class PortStream : Stream
{ {
/// <summary> /// <summary>
/// Optional bytes available hint if the transport can provide it. /// Optional bytes available hint if the transport can provide it.
/// </summary> /// </summary>
public virtual long? BytesAvailable => null; public virtual long? BytesAvailable => null;
/// <summary> /// <summary>
/// Close the underlying transport and release resources. /// Close the underlying transport and release resources.
/// </summary> /// </summary>
public abstract override void Close(); public abstract override void Close();
/// <summary> /// <summary>
/// Ensure the port is opened and ready. Idempotent. May perform device-specific /// Ensure the port is opened and ready. Idempotent. May perform device-specific
/// handshakes. /// handshakes.
/// </summary> /// </summary>
public abstract ValueTask OpenAsync(CancellationToken cancellationToken = default); public abstract ValueTask OpenAsync(CancellationToken cancellationToken = default);
// Concrete transports must implement Stream's abstract members. // Concrete transports must implement Stream's abstract members.
// Seek-related members are typically not supported and should throw NotSupportedException. // Seek-related members are typically not supported and should throw NotSupportedException.
} }
} }
+192 -192
View File
@@ -1,192 +1,192 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="CsTypeMapper" // file="CsTypeMapper"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
namespace GenerateGL.CodeGen namespace GenerateGL.CodeGen
{ {
internal static class CsTypeMapper internal static class CsTypeMapper
{ {
public static string MapDelegateParameter( public static string MapDelegateParameter(
GlParam param GlParam param
) )
{ {
int pointerLevel = param.PointerLevel; int pointerLevel = param.PointerLevel;
if (param.CType.Equals("GLchar", StringComparison.Ordinal) && pointerLevel > 0) { if (param.CType.Equals("GLchar", StringComparison.Ordinal) && pointerLevel > 0) {
return $"byte{new string('*', pointerLevel)}"; return $"byte{new string('*', pointerLevel)}";
} }
string mapped = MapType(param.CType, allowVoid:false); string mapped = MapType(param.CType, allowVoid:false);
if (pointerLevel > 0) if (pointerLevel > 0)
{ {
if (mapped == "void") { if (mapped == "void") {
return $"void{new string('*', pointerLevel)}"; return $"void{new string('*', pointerLevel)}";
} }
return $"{mapped}{new string('*', pointerLevel)}"; return $"{mapped}{new string('*', pointerLevel)}";
} }
return mapped; return mapped;
} }
public static string MapDelegateReturn( public static string MapDelegateReturn(
string cType, string cType,
int pointerLevel int pointerLevel
) )
{ {
string mapped = MapType(cType, allowVoid:true); string mapped = MapType(cType, allowVoid:true);
if (pointerLevel > 0) { if (pointerLevel > 0) {
return $"{mapped}{new string('*', pointerLevel)}"; return $"{mapped}{new string('*', pointerLevel)}";
} }
return mapped; return mapped;
} }
public static string MapParameter( public static string MapParameter(
GlParam param GlParam param
) )
{ {
if (param.PointerLevel == 0) if (param.PointerLevel == 0)
{ {
if (param.CType.Equals("GLenum", StringComparison.Ordinal) && if (param.CType.Equals("GLenum", StringComparison.Ordinal) &&
LooksLikeShaderType(param.Name) && LooksLikeShaderType(param.Name) &&
!param.IsPointer) { !param.IsPointer) {
return "ShaderType"; return "ShaderType";
} }
if (param.CType.Equals("GLuint", StringComparison.Ordinal) && if (param.CType.Equals("GLuint", StringComparison.Ordinal) &&
LooksLikeHandle(param.Name) && LooksLikeHandle(param.Name) &&
!param.IsPointer) { !param.IsPointer) {
return "GlHandle"; return "GlHandle";
} }
} }
int pointerLevel = param.PointerLevel; int pointerLevel = param.PointerLevel;
if (param.CType.Equals("GLchar", StringComparison.Ordinal) && pointerLevel > 0) { if (param.CType.Equals("GLchar", StringComparison.Ordinal) && pointerLevel > 0) {
return $"byte{new string('*', pointerLevel)}"; return $"byte{new string('*', pointerLevel)}";
} }
string mapped = MapType(param.CType, allowVoid:false); string mapped = MapType(param.CType, allowVoid:false);
if (pointerLevel > 0) if (pointerLevel > 0)
{ {
if (mapped == "void") { if (mapped == "void") {
return $"void{new string('*', pointerLevel)}"; return $"void{new string('*', pointerLevel)}";
} }
return $"{mapped}{new string('*', pointerLevel)}"; return $"{mapped}{new string('*', pointerLevel)}";
} }
return mapped; return mapped;
} }
public static string MapReturn( public static string MapReturn(
string cType, string cType,
int pointerLevel int pointerLevel
) )
{ {
string mapped = MapType(cType, allowVoid:true); string mapped = MapType(cType, allowVoid:true);
if (pointerLevel > 0) { if (pointerLevel > 0) {
return $"{mapped}{new string('*', pointerLevel)}"; return $"{mapped}{new string('*', pointerLevel)}";
} }
return mapped; return mapped;
} }
public static bool TryMapConstPointerToSpan( public static bool TryMapConstPointerToSpan(
GlParam param, GlParam param,
out string spanElementType out string spanElementType
) )
{ {
spanElementType = string.Empty; spanElementType = string.Empty;
if (!param.IsConst || param.PointerLevel != 1) { if (!param.IsConst || param.PointerLevel != 1) {
return false; return false;
} }
string mapped = MapType(param.CType, allowVoid:false); string mapped = MapType(param.CType, allowVoid:false);
if (mapped is "void") { if (mapped is "void") {
return false; return false;
} }
spanElementType = mapped; spanElementType = mapped;
return true; return true;
} }
private static bool LooksLikeHandle( private static bool LooksLikeHandle(
string name string name
) )
{ {
if (string.IsNullOrWhiteSpace(name)) { if (string.IsNullOrWhiteSpace(name)) {
return false; return false;
} }
return name.Contains("program", StringComparison.OrdinalIgnoreCase) || return name.Contains("program", StringComparison.OrdinalIgnoreCase) ||
name.Contains("shader", StringComparison.OrdinalIgnoreCase) || name.Contains("shader", StringComparison.OrdinalIgnoreCase) ||
name.Contains("buffer", StringComparison.OrdinalIgnoreCase) || name.Contains("buffer", StringComparison.OrdinalIgnoreCase) ||
name.Contains("texture", StringComparison.OrdinalIgnoreCase) || name.Contains("texture", StringComparison.OrdinalIgnoreCase) ||
name.Contains("framebuffer", StringComparison.OrdinalIgnoreCase) || name.Contains("framebuffer", StringComparison.OrdinalIgnoreCase) ||
name.Contains("renderbuffer", StringComparison.OrdinalIgnoreCase) || name.Contains("renderbuffer", StringComparison.OrdinalIgnoreCase) ||
name.Contains("vertexarray", StringComparison.OrdinalIgnoreCase) || name.Contains("vertexarray", StringComparison.OrdinalIgnoreCase) ||
name.Contains("query", StringComparison.OrdinalIgnoreCase) || name.Contains("query", StringComparison.OrdinalIgnoreCase) ||
name.Contains("sampler", StringComparison.OrdinalIgnoreCase) || name.Contains("sampler", StringComparison.OrdinalIgnoreCase) ||
name.Contains("transformfeedback", StringComparison.OrdinalIgnoreCase); name.Contains("transformfeedback", StringComparison.OrdinalIgnoreCase);
} }
private static bool LooksLikeShaderType( private static bool LooksLikeShaderType(
string name string name
) )
{ {
if (string.IsNullOrWhiteSpace(name)) { if (string.IsNullOrWhiteSpace(name)) {
return false; return false;
} }
return name.Contains("shadertype", StringComparison.OrdinalIgnoreCase) || return name.Contains("shadertype", StringComparison.OrdinalIgnoreCase) ||
name.Equals("type", StringComparison.OrdinalIgnoreCase); name.Equals("type", StringComparison.OrdinalIgnoreCase);
} }
private static string MapType( private static string MapType(
string cType, string cType,
bool allowVoid bool allowVoid
) )
{ {
return cType switch return cType switch
{ {
"void" when allowVoid => "void", "void" when allowVoid => "void",
"GLenum" or "GLbitfield" => "uint", "GLenum" or "GLbitfield" => "uint",
"GLboolean" => "byte", "GLboolean" => "byte",
"GLuint" or "GLhandleARB" => "uint", "GLuint" or "GLhandleARB" => "uint",
"GLint" or "GLsizei" or "GLfixed" => "int", "GLint" or "GLsizei" or "GLfixed" => "int",
"GLshort" => "short", "GLshort" => "short",
"GLbyte" => "sbyte", "GLbyte" => "sbyte",
"GLubyte" => "byte", "GLubyte" => "byte",
"GLushort" => "ushort", "GLushort" => "ushort",
"GLfloat" => "float", "GLfloat" => "float",
"GLdouble" => "double", "GLdouble" => "double",
"GLclampf" or "GLclampd" => "float", "GLclampf" or "GLclampd" => "float",
"GLintptr" => "nint", "GLintptr" => "nint",
"GLsizeiptr" => "nuint", "GLsizeiptr" => "nuint",
"GLchar" => "byte", "GLchar" => "byte",
"GLsync" => "GLsync", "GLsync" => "GLsync",
_ => allowVoid && cType == "void" ? "void" : "IntPtr" _ => allowVoid && cType == "void" ? "void" : "IntPtr"
}; };
} }
} }
} }
+422 -422
View File
@@ -1,422 +1,422 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// file="Generator" // file="Generator"
// Project: StgSharp // Project: StgSharp
// AuthorGroup: Nitload // AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved. // Copyright (c) Nitload. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights // in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: // furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
// //
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.Collections.Frozen; using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
namespace GenerateGL.CodeGen namespace GenerateGL.CodeGen
{ {
internal sealed class Generator internal sealed class Generator
{ {
private static readonly string[] TargetVersions = { private static readonly string[] TargetVersions = {
"3.3", "4.0", "4.1", "4.2", "4.3", "4.4", "4.5", "4.6" "3.3", "4.0", "4.1", "4.2", "4.3", "4.4", "4.5", "4.6"
}; };
private static readonly FrozenDictionary<string, string> ReservedNames = private static readonly FrozenDictionary<string, string> ReservedNames =
new Dictionary<string, string>(StringComparer.Ordinal) { 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" } { "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); }.ToFrozenDictionary(StringComparer.Ordinal);
private readonly IReadOnlyDictionary<string, GlCommand> _commands; private readonly IReadOnlyDictionary<string, GlCommand> _commands;
private readonly IReadOnlyList<GlFeature> _features; private readonly IReadOnlyList<GlFeature> _features;
private readonly string _outputRoot; private readonly string _outputRoot;
public Generator( public Generator(
IReadOnlyDictionary<string, GlCommand> commands, IReadOnlyDictionary<string, GlCommand> commands,
IReadOnlyList<GlFeature> features, IReadOnlyList<GlFeature> features,
string outputRoot string outputRoot
) )
{ {
_commands = commands; _commands = commands;
_features = features; _features = features;
_outputRoot = outputRoot; _outputRoot = outputRoot;
} }
public void Run() public void Run()
{ {
if (Directory.Exists(_outputRoot)) { if (Directory.Exists(_outputRoot)) {
Directory.Delete(_outputRoot, recursive:true); Directory.Delete(_outputRoot, recursive:true);
} }
Directory.CreateDirectory(_outputRoot); Directory.CreateDirectory(_outputRoot);
List<GlCommand> all = new List<GlCommand>(); List<GlCommand> all = new List<GlCommand>();
HashSet<string> previous = new HashSet<string>(StringComparer.Ordinal); HashSet<string> previous = new HashSet<string>(StringComparer.Ordinal);
foreach (string v in TargetVersions) foreach (string v in TargetVersions)
{ {
IReadOnlyList<GlCommand> cmdsUpTo = CollectCommandsUpToVersion(v); IReadOnlyList<GlCommand> cmdsUpTo = CollectCommandsUpToVersion(v);
List<GlCommand> versionCmds = cmdsUpTo.Where(c => !previous.Contains(c.Name)) List<GlCommand> versionCmds = cmdsUpTo.Where(c => !previous.Contains(c.Name))
.ToList(); .ToList();
foreach (GlCommand cmd in versionCmds) { foreach (GlCommand cmd in versionCmds) {
previous.Add(cmd.Name); previous.Add(cmd.Name);
} }
all.AddRange(versionCmds); all.AddRange(versionCmds);
WriteOpenGLFunctionFile(v, versionCmds); WriteOpenGLFunctionFile(v, versionCmds);
WriteGlContextFile(v, versionCmds); WriteGlContextFile(v, versionCmds);
} }
WriteMergedGlContextFile(all); WriteMergedGlContextFile(all);
WriteCommandHistoryFile(BuildCommandHistory()); WriteCommandHistoryFile(BuildCommandHistory());
} }
private IReadOnlyDictionary<string, List<CommandHistoryEntry>> BuildCommandHistory() private IReadOnlyDictionary<string, List<CommandHistoryEntry>> BuildCommandHistory()
{ {
Dictionary<string, List<CommandHistoryEntry>> history = new(StringComparer.Ordinal); Dictionary<string, List<CommandHistoryEntry>> history = new(StringComparer.Ordinal);
IEnumerable<GlFeature> ordered = _features.Where(f => Version.TryParse(f.Version, out _)) IEnumerable<GlFeature> ordered = _features.Where(f => Version.TryParse(f.Version, out _))
.OrderBy(f => Version.Parse(f.Version)); .OrderBy(f => Version.Parse(f.Version));
foreach (GlFeature feature in ordered) foreach (GlFeature feature in ordered)
{ {
string version = feature.Version; string version = feature.Version;
foreach (string name in feature.RequiresCommands.Distinct(StringComparer.Ordinal)) foreach (string name in feature.RequiresCommands.Distinct(StringComparer.Ordinal))
{ {
if (!_commands.ContainsKey(name) || ShouldSkip(name)) if (!_commands.ContainsKey(name) || ShouldSkip(name))
{ {
continue; continue;
} }
if (!history.TryGetValue(name, out List<CommandHistoryEntry>? list)) if (!history.TryGetValue(name, out List<CommandHistoryEntry>? list))
{ {
list = new List<CommandHistoryEntry>(); list = new List<CommandHistoryEntry>();
history[name] = list; history[name] = list;
} }
if (list.Count == 0 || if (list.Count == 0 ||
list[^1].Action != CommandHistoryAction.Added || list[^1].Action != CommandHistoryAction.Added ||
list[^1].Version != version) { list[^1].Version != version) {
list.Add(new CommandHistoryEntry(version, CommandHistoryAction.Added)); list.Add(new CommandHistoryEntry(version, CommandHistoryAction.Added));
} }
} }
foreach (string name in feature.RemovedCommands.Distinct(StringComparer.Ordinal)) foreach (string name in feature.RemovedCommands.Distinct(StringComparer.Ordinal))
{ {
if (!_commands.ContainsKey(name) || ShouldSkip(name)) if (!_commands.ContainsKey(name) || ShouldSkip(name))
{ {
continue; continue;
} }
if (!history.TryGetValue(name, out List<CommandHistoryEntry>? list)) if (!history.TryGetValue(name, out List<CommandHistoryEntry>? list))
{ {
list = new List<CommandHistoryEntry>(); list = new List<CommandHistoryEntry>();
history[name] = list; history[name] = list;
} }
if (list.Count == 0 || if (list.Count == 0 ||
list[^1].Action != CommandHistoryAction.Removed || list[^1].Action != CommandHistoryAction.Removed ||
list[^1].Version != version) { list[^1].Version != version) {
list.Add(new CommandHistoryEntry(version, CommandHistoryAction.Removed)); list.Add(new CommandHistoryEntry(version, CommandHistoryAction.Removed));
} }
} }
} }
return history; return history;
} }
private IReadOnlyList<GlCommand> CollectCommandsUpToVersion( private IReadOnlyList<GlCommand> CollectCommandsUpToVersion(
string version string version
) )
{ {
Version target = Version.Parse(version); Version target = Version.Parse(version);
IEnumerable<GlFeature> ordered = _features.Where(f => Version.TryParse(f.Version, out Version? fv) && IEnumerable<GlFeature> ordered = _features.Where(f => Version.TryParse(f.Version, out Version? fv) &&
fv <= target) fv <= target)
.OrderBy(f => Version.Parse(f.Version)); .OrderBy(f => Version.Parse(f.Version));
HashSet<string> active = new HashSet<string>(StringComparer.Ordinal); HashSet<string> active = new HashSet<string>(StringComparer.Ordinal);
foreach (GlFeature feature in ordered) foreach (GlFeature feature in ordered)
{ {
foreach (string name in feature.RequiresCommands.Distinct(StringComparer.Ordinal)) foreach (string name in feature.RequiresCommands.Distinct(StringComparer.Ordinal))
{ {
if (_commands.ContainsKey(name)) { if (_commands.ContainsKey(name)) {
active.Add(name); active.Add(name);
} }
} }
foreach (string name in feature.RemovedCommands.Distinct(StringComparer.Ordinal)) { foreach (string name in feature.RemovedCommands.Distinct(StringComparer.Ordinal)) {
active.Remove(name); active.Remove(name);
} }
} }
return active.Select(name => _commands.TryGetValue(name, out GlCommand? c) ? c : null) return active.Select(name => _commands.TryGetValue(name, out GlCommand? c) ? c : null)
.Where(c => c != null)! .Where(c => c != null)!
.Cast<GlCommand>() .Cast<GlCommand>()
.ToList(); .ToList();
} }
private static string DelegateSignature( private static string DelegateSignature(
string ret, string ret,
IReadOnlyList<string> paramTypes IReadOnlyList<string> paramTypes
) )
{ {
if (paramTypes.Count == 0) { if (paramTypes.Count == 0) {
return $"delegate*<{ret}>"; return $"delegate*<{ret}>";
} }
return $"delegate*<{string.Join(", ", paramTypes)}, {ret}>"; return $"delegate*<{string.Join(", ", paramTypes)}, {ret}>";
} }
private static string SafeName( private static string SafeName(
string name string name
) )
{ {
if (string.IsNullOrWhiteSpace(name)) { if (string.IsNullOrWhiteSpace(name)) {
return "arg"; return "arg";
} }
if (ReservedNames.TryGetValue(name, out string? mapped)) { if (ReservedNames.TryGetValue(name, out string? mapped)) {
return mapped; return mapped;
} }
return name; return name;
} }
private static bool ShouldSkip( private static bool ShouldSkip(
string name string name
) )
{ {
// No skipping; include legacy/fixed-function APIs as well. // No skipping; include legacy/fixed-function APIs as well.
return false; return false;
} }
private static string TrimGlPrefix( private static string TrimGlPrefix(
string name string name
) )
{ {
if (name.StartsWith("gl", StringComparison.Ordinal) && name.Length > 2) { if (name.StartsWith("gl", StringComparison.Ordinal) && name.Length > 2) {
return name.Substring(2); return name.Substring(2);
} }
return name; return name;
} }
private void WriteCommandHistoryFile( private void WriteCommandHistoryFile(
IReadOnlyDictionary<string, List<CommandHistoryEntry>> history IReadOnlyDictionary<string, List<CommandHistoryEntry>> history
) )
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.AppendLine("// <auto-generated>"); sb.AppendLine("// <auto-generated>");
sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}"); sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}");
sb.AppendLine("// Command availability history"); sb.AppendLine("// Command availability history");
sb.AppendLine("// </auto-generated>"); sb.AppendLine("// </auto-generated>");
sb.AppendLine("using System;\nusing System.Collections.Frozen;\nusing System.Collections.Generic;\n"); sb.AppendLine("using System;\nusing System.Collections.Frozen;\nusing System.Collections.Generic;\n");
sb.AppendLine("namespace StgSharp.Graphics.OpenGL"); sb.AppendLine("namespace StgSharp.Graphics.OpenGL");
sb.AppendLine("{"); sb.AppendLine("{");
sb.AppendLine(" internal enum CommandHistoryAction { Added, Removed }"); sb.AppendLine(" internal enum CommandHistoryAction { Added, Removed }");
sb.AppendLine(); sb.AppendLine();
sb.AppendLine(" internal readonly record struct CommandHistoryEntry(string Version, CommandHistoryAction Action);"); sb.AppendLine(" internal readonly record struct CommandHistoryEntry(string Version, CommandHistoryAction Action);");
sb.AppendLine(); sb.AppendLine();
sb.AppendLine(" internal static class GlCommandHistory"); sb.AppendLine(" internal static class GlCommandHistory");
sb.AppendLine(" {"); sb.AppendLine(" {");
sb.AppendLine(" internal static readonly FrozenDictionary<string, CommandHistoryEntry[]> History = new Dictionary<string, CommandHistoryEntry[]>(StringComparer.Ordinal)"); sb.AppendLine(" internal static readonly FrozenDictionary<string, CommandHistoryEntry[]> History = new Dictionary<string, CommandHistoryEntry[]>(StringComparer.Ordinal)");
sb.AppendLine(" {"); sb.AppendLine(" {");
foreach (KeyValuePair<string, List<CommandHistoryEntry>> kvp in history.OrderBy(k => k.Key, StringComparer.Ordinal)) foreach (KeyValuePair<string, List<CommandHistoryEntry>> kvp in history.OrderBy(k => k.Key, StringComparer.Ordinal))
{ {
string events = string.Join(", ", kvp.Value string events = string.Join(", ", kvp.Value
.OrderBy(v => Version.Parse(v.Version)) .OrderBy(v => Version.Parse(v.Version))
.Select(v => $"new CommandHistoryEntry(\"{v.Version}\", CommandHistoryAction.{v.Action})")); .Select(v => $"new CommandHistoryEntry(\"{v.Version}\", CommandHistoryAction.{v.Action})"));
sb.AppendLine($" {{ \"{kvp.Key}\", new CommandHistoryEntry[] {{ {events} }} }},"); sb.AppendLine($" {{ \"{kvp.Key}\", new CommandHistoryEntry[] {{ {events} }} }},");
} }
sb.AppendLine(" }.ToFrozenDictionary(StringComparer.Ordinal);"); sb.AppendLine(" }.ToFrozenDictionary(StringComparer.Ordinal);");
sb.AppendLine(" }"); sb.AppendLine(" }");
sb.AppendLine("}"); sb.AppendLine("}");
string fileName = Path.Combine(_outputRoot, "glCommandHistory.cs"); string fileName = Path.Combine(_outputRoot, "glCommandHistory.cs");
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8); File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
} }
private void WriteGlContextFile( private void WriteGlContextFile(
string version, string version,
IReadOnlyList<GlCommand> commands IReadOnlyList<GlCommand> commands
) )
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.AppendLine("// <auto-generated>"); sb.AppendLine("// <auto-generated>");
sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}"); sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}");
sb.AppendLine($"// Version: {version}"); sb.AppendLine($"// Version: {version}");
sb.AppendLine("// </auto-generated>"); sb.AppendLine("// </auto-generated>");
sb.AppendLine("using System;\nusing System.Runtime.CompilerServices;\n"); sb.AppendLine("using System;\nusing System.Runtime.CompilerServices;\n");
sb.AppendLine("namespace StgSharp.Graphics.OpenGL"); sb.AppendLine("namespace StgSharp.Graphics.OpenGL");
sb.AppendLine("{"); sb.AppendLine("{");
sb.AppendLine(" public unsafe partial struct OpenglContext"); sb.AppendLine(" public unsafe partial struct OpenglContext");
sb.AppendLine(" {"); sb.AppendLine(" {");
foreach (GlCommand cmd in commands.OrderBy(c => c!.Name, StringComparer.Ordinal)) foreach (GlCommand cmd in commands.OrderBy(c => c!.Name, StringComparer.Ordinal))
{ {
string ret = CsTypeMapper.MapDelegateReturn(cmd.ReturnType, cmd.ReturnPointerLevel); string ret = CsTypeMapper.MapDelegateReturn(cmd.ReturnType, cmd.ReturnPointerLevel);
List<string> paramTypes = new List<string>(); List<string> paramTypes = new List<string>();
foreach (GlParam p in cmd.Parameters) foreach (GlParam p in cmd.Parameters)
{ {
string mapped = CsTypeMapper.MapDelegateParameter(p); string mapped = CsTypeMapper.MapDelegateParameter(p);
paramTypes.Add(mapped); paramTypes.Add(mapped);
} }
string decl = DelegateSignature(ret, paramTypes); string decl = DelegateSignature(ret, paramTypes);
sb.AppendLine($" internal unsafe {decl} {cmd.Name};"); sb.AppendLine($" internal unsafe {decl} {cmd.Name};");
} }
sb.AppendLine(" }"); sb.AppendLine(" }");
sb.AppendLine("}"); sb.AppendLine("}");
string fileName = Path.Combine(_outputRoot, $"glContext.v{version.Replace('.', '_')}.cs"); string fileName = Path.Combine(_outputRoot, $"glContext.v{version.Replace('.', '_')}.cs");
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8); File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
} }
private void WriteMergedGlContextFile( private void WriteMergedGlContextFile(
IReadOnlyList<GlCommand> commands IReadOnlyList<GlCommand> commands
) )
{ {
List<GlCommand> distinct = commands.Where(c => !ShouldSkip(c.Name)) List<GlCommand> distinct = commands.Where(c => !ShouldSkip(c.Name))
.GroupBy(c => c.Name, StringComparer.Ordinal) .GroupBy(c => c.Name, StringComparer.Ordinal)
.Select(g => g.First()) .Select(g => g.First())
.OrderBy(c => c.Name, StringComparer.Ordinal) .OrderBy(c => c.Name, StringComparer.Ordinal)
.ToList(); .ToList();
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.AppendLine("// <auto-generated>"); sb.AppendLine("// <auto-generated>");
sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}"); sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}");
sb.AppendLine("// Aggregated GL3+ commands"); sb.AppendLine("// Aggregated GL3+ commands");
sb.AppendLine("// </auto-generated>"); sb.AppendLine("// </auto-generated>");
sb.AppendLine("using System;\nusing System.Runtime.CompilerServices;\n"); sb.AppendLine("using System;\nusing System.Runtime.CompilerServices;\n");
sb.AppendLine("namespace StgSharp.Graphics.OpenGL"); sb.AppendLine("namespace StgSharp.Graphics.OpenGL");
sb.AppendLine("{"); sb.AppendLine("{");
sb.AppendLine(" public unsafe partial struct OpenglContext"); sb.AppendLine(" public unsafe partial struct OpenglContext");
sb.AppendLine(" {"); sb.AppendLine(" {");
foreach (GlCommand cmd in distinct) foreach (GlCommand cmd in distinct)
{ {
string ret = CsTypeMapper.MapDelegateReturn(cmd.ReturnType, cmd.ReturnPointerLevel); string ret = CsTypeMapper.MapDelegateReturn(cmd.ReturnType, cmd.ReturnPointerLevel);
List<string> paramTypes = new List<string>(); List<string> paramTypes = new List<string>();
foreach (GlParam p in cmd.Parameters) foreach (GlParam p in cmd.Parameters)
{ {
string mapped = CsTypeMapper.MapDelegateParameter(p); string mapped = CsTypeMapper.MapDelegateParameter(p);
paramTypes.Add(mapped); paramTypes.Add(mapped);
} }
string decl = DelegateSignature(ret, paramTypes); string decl = DelegateSignature(ret, paramTypes);
sb.AppendLine($" internal unsafe {decl} {cmd.Name};"); sb.AppendLine($" internal unsafe {decl} {cmd.Name};");
} }
sb.AppendLine(" }"); sb.AppendLine(" }");
sb.AppendLine("}"); sb.AppendLine("}");
string fileName = Path.Combine(_outputRoot, "glContext.cs"); string fileName = Path.Combine(_outputRoot, "glContext.cs");
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8); File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
} }
private void WriteOpenGLFunctionFile( private void WriteOpenGLFunctionFile(
string version, string version,
IReadOnlyList<GlCommand> commands IReadOnlyList<GlCommand> commands
) )
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.AppendLine("// <auto-generated>"); sb.AppendLine("// <auto-generated>");
sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}"); sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}");
sb.AppendLine($"// Version: {version}"); sb.AppendLine($"// Version: {version}");
sb.AppendLine("// </auto-generated>"); sb.AppendLine("// </auto-generated>");
sb.AppendLine("using System;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\nusing StgSharp.Graphics.OpenGL;\n"); 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("namespace StgSharp.Graphics.OpenGL");
sb.AppendLine("{"); sb.AppendLine("{");
sb.AppendLine(" public unsafe partial class OpenGLFunction"); sb.AppendLine(" public unsafe partial class OpenGLFunction");
sb.AppendLine(" {"); sb.AppendLine(" {");
foreach (GlCommand cmd in commands.OrderBy(c => c!.Name, StringComparer.Ordinal)) foreach (GlCommand cmd in commands.OrderBy(c => c!.Name, StringComparer.Ordinal))
{ {
string ret = CsTypeMapper.MapReturn(cmd.ReturnType, cmd.ReturnPointerLevel); string ret = CsTypeMapper.MapReturn(cmd.ReturnType, cmd.ReturnPointerLevel);
string rawName = cmd.Name; // original glXxx string rawName = cmd.Name; // original glXxx
string methodName = TrimGlPrefix(rawName); string methodName = TrimGlPrefix(rawName);
List<string> paramList = new List<string>(); List<string> paramList = new List<string>();
List<string> callArgs = new List<string>(); List<string> callArgs = new List<string>();
List<(string Type,string Name)> pinned = new(); List<(string Type,string Name)> pinned = new();
foreach (GlParam p in cmd.Parameters) foreach (GlParam p in cmd.Parameters)
{ {
string pname = SafeName(p.Name); string pname = SafeName(p.Name);
if (CsTypeMapper.TryMapConstPointerToSpan(p, out string spanElementType)) if (CsTypeMapper.TryMapConstPointerToSpan(p, out string spanElementType))
{ {
paramList.Add($"ReadOnlySpan<{spanElementType}> {pname}"); paramList.Add($"ReadOnlySpan<{spanElementType}> {pname}");
pinned.Add((spanElementType, pname)); pinned.Add((spanElementType, pname));
callArgs.Add($"{pname}Ptr"); callArgs.Add($"{pname}Ptr");
} else } else
{ {
string mapped = CsTypeMapper.MapParameter(p); string mapped = CsTypeMapper.MapParameter(p);
paramList.Add($"{mapped} {pname}"); paramList.Add($"{mapped} {pname}");
if (mapped == "GlHandle") if (mapped == "GlHandle")
{ {
callArgs.Add($"{pname}.Value"); callArgs.Add($"{pname}.Value");
} else } else
{ {
callArgs.Add(pname); callArgs.Add(pname);
} }
} }
} }
string signature = string.Join(", ", paramList); string signature = string.Join(", ", paramList);
string call = string.Join(", ", callArgs); string call = string.Join(", ", callArgs);
sb.AppendLine($" [MethodImpl(MethodImplOptions.AggressiveInlining)]"); sb.AppendLine($" [MethodImpl(MethodImplOptions.AggressiveInlining)]");
sb.AppendLine($" public {ret} {methodName}({signature})"); sb.AppendLine($" public {ret} {methodName}({signature})");
sb.AppendLine(" {"); sb.AppendLine(" {");
string prefix = ret == "void" ? string.Empty : "return "; string prefix = ret == "void" ? string.Empty : "return ";
if (pinned.Count == 0) if (pinned.Count == 0)
{ {
sb.AppendLine($" {prefix}Context.{rawName}({call});"); sb.AppendLine($" {prefix}Context.{rawName}({call});");
} else } else
{ {
string indent = " "; string indent = " ";
foreach ((string Type, string Name) pin in pinned) foreach ((string Type, string Name) pin in pinned)
{ {
sb.AppendLine($"{indent}fixed ({pin.Type}* {pin.Name}Ptr = {pin.Name})"); sb.AppendLine($"{indent}fixed ({pin.Type}* {pin.Name}Ptr = {pin.Name})");
sb.AppendLine($"{indent}{{"); sb.AppendLine($"{indent}{{");
indent += " "; indent += " ";
} }
sb.AppendLine($"{indent}{prefix}Context.{rawName}({call});"); sb.AppendLine($"{indent}{prefix}Context.{rawName}({call});");
for (int i = 0; i < pinned.Count; i++) for (int i = 0; i < pinned.Count; i++)
{ {
indent = indent.Substring(0, indent.Length - 4); indent = indent.Substring(0, indent.Length - 4);
sb.AppendLine($"{indent}}}"); sb.AppendLine($"{indent}}}");
} }
} }
sb.AppendLine(" }"); sb.AppendLine(" }");
sb.AppendLine(); sb.AppendLine();
} }
sb.AppendLine(" }"); sb.AppendLine(" }");
sb.AppendLine("}"); sb.AppendLine("}");
string fileName = Path.Combine(_outputRoot, $"glFunction.v{version.Replace('.', '_')}.cs"); string fileName = Path.Combine(_outputRoot, $"glFunction.v{version.Replace('.', '_')}.cs");
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8); File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
} }
private enum CommandHistoryAction private enum CommandHistoryAction
{ {
Added, Added,
Removed Removed
} }
private readonly record struct CommandHistoryEntry( private readonly record struct CommandHistoryEntry(
string Version, string Version,
CommandHistoryAction Action CommandHistoryAction Action
); );
} }
} }

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