├── .editorconfig ├── .github ├── dependabot.yaml └── workflows │ ├── build-debug.yaml │ ├── build-release.yaml │ ├── prevent-github-change.yaml │ ├── stale.yaml │ └── toc.yaml ├── .gitignore ├── LICENSE ├── MasterMemory.sln ├── README.md ├── sandbox ├── Benchmark │ ├── Benchmark.csproj │ ├── Program.cs │ └── Utils │ │ └── Helper.cs ├── ConsoleApp │ ├── ConsoleApp.csproj │ └── Program.cs ├── GeneratorSandbox │ ├── GeneratorSandbox.csproj │ └── Program.cs └── PerfTest2 │ ├── Engines │ ├── Dictionary_Test.cs │ ├── ITest.cs │ ├── LiteDB_Test.cs │ ├── MasterMemory_Test.cs │ ├── RavenDB_Test.cs │ └── SQLite_Test.cs │ ├── Generated │ ├── DatabaseBuilder.cs │ ├── ImmutableBuilder.cs │ ├── MasterMemoryResolver.cs │ ├── MemoryDatabase.cs │ └── Tables │ │ └── TestDocTable.cs │ ├── PerfTest2.csproj │ ├── Program.cs │ └── Utils │ └── Helper.cs ├── src ├── MasterMemory.Annotations │ ├── Attributes.cs │ └── MasterMemory.Annotations.csproj ├── MasterMemory.SourceGenerator │ ├── DiagnosticDescriptors.cs │ ├── GeneratorCore │ │ ├── CodeGenerator.cs │ │ ├── DatabaseBuilderTemplate.cs │ │ ├── DatabaseBuilderTemplate.tt │ │ ├── GenerationContext.cs │ │ ├── ImmutableBuilderTemplate.cs │ │ ├── ImmutableBuilderTemplate.tt │ │ ├── MemoryDatabaseTemplate.cs │ │ ├── MemoryDatabaseTemplate.tt │ │ ├── MessagePackResolverTemplate.cs │ │ ├── MessagePackResolverTemplate.tt │ │ ├── TableTemplate.cs │ │ ├── TableTemplate.tt │ │ └── Template.cs │ ├── MasterMemory.GeneratorCore.csproj │ ├── MasterMemory.SourceGenerator.csproj │ ├── MasterMemoryGenerator.cs │ ├── MasterMemoryGeneratorOptions.cs │ ├── Polyfill │ │ └── System.CodeDom.cs │ ├── Properties │ │ └── launchSettings.json │ └── Utility │ │ ├── EquatableArray.cs │ │ └── IgnoreEquality.cs ├── MasterMemory.Unity │ ├── Assets │ │ ├── NuGet.config │ │ ├── NuGet.config.meta │ │ ├── Packages.meta │ │ ├── Scenes.meta │ │ ├── Scenes │ │ │ ├── Main.unity │ │ │ └── Main.unity.meta │ │ ├── Scripts.meta │ │ ├── Scripts │ │ │ ├── NewBehaviourScript.cs │ │ │ └── NewBehaviourScript.cs.meta │ │ ├── packages.config │ │ └── packages.config.meta │ ├── Packages │ │ ├── manifest.json │ │ └── packages-lock.json │ └── ProjectSettings │ │ ├── AudioManager.asset │ │ ├── ClusterInputManager.asset │ │ ├── DynamicsManager.asset │ │ ├── EditorBuildSettings.asset │ │ ├── EditorSettings.asset │ │ ├── GraphicsSettings.asset │ │ ├── InputManager.asset │ │ ├── MemorySettings.asset │ │ ├── NavMeshAreas.asset │ │ ├── NetworkManager.asset │ │ ├── PackageManagerSettings.asset │ │ ├── Physics2DSettings.asset │ │ ├── PresetManager.asset │ │ ├── ProjectSettings.asset │ │ ├── ProjectVersion.txt │ │ ├── QualitySettings.asset │ │ ├── SceneTemplateSettings.json │ │ ├── TagManager.asset │ │ ├── TimeManager.asset │ │ ├── UnityConnectSettings.asset │ │ ├── VFXManager.asset │ │ ├── VersionControlSettings.asset │ │ └── XRSettings.asset └── MasterMemory │ ├── DatabaseBuilderBase.cs │ ├── DatabaseBuilderBaseExtensions.cs │ ├── IValidatable.cs │ ├── ImmutableBuilderBase.cs │ ├── Internal │ ├── BinarySearch.cs │ ├── ByteBufferWriter.cs │ ├── ExpandableArray.cs │ ├── HeaderFormatterResolver.cs │ └── InternStringResolver.cs │ ├── MasterMemory.csproj │ ├── MemoryDatabaseBase.cs │ ├── Meta │ └── Meta.cs │ ├── RangeView.cs │ ├── TableBase.cs │ ├── Validation │ ├── ExpressionDumper.cs │ ├── ExpressionParameterNameModifier.cs │ ├── ITableUniqueValidate.cs │ ├── ReferenceSet.cs │ ├── ValidatableSet.Sequential.cs │ ├── ValidatableSet.Sequential.tt │ ├── ValidatableSet.cs │ ├── ValidateResult.cs │ ├── ValidationDatabase.cs │ └── Validator.cs │ ├── _InternalVisibleTo.cs │ ├── _MessagePackResolver.cs │ └── bin │ └── Debug │ └── netstandard2.0 │ ├── package.json │ └── package.json.meta └── tests ├── MasterMemory.SourceGenerator.Tests ├── AssemblyAtrributeTest.cs ├── DiagnosticsTest.cs ├── GenerateTest.cs ├── IncrementalGeneratorTest.cs ├── MasterMemory.SourceGenerator.Tests.csproj ├── TestBase.cs └── Utility │ ├── CSharpGeneratorRunner.cs │ └── CodeGeneratorHelper.cs └── MasterMemory.Tests ├── BinarySearchTest.cs ├── DatabaseTest.cs ├── IssueTest.cs ├── MasterMemory.Tests.csproj ├── MemoryKeyTest.cs ├── MemoryTest.cs ├── MessagePackResolver.cs ├── MetaTest.cs ├── RangeViewTest.cs ├── TestStructures ├── PersonModel.cs ├── QuestMaster.cs ├── Sample.cs ├── SkillMaster.cs ├── TestMaster.cs └── UserLevel.cs └── ValidatorTest.cs /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = space 8 | indent_size = 2 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | # Visual Studio Spell checker configs (https://learn.microsoft.com/en-us/visualstudio/ide/text-spell-checker?view=vs-2022#how-to-customize-the-spell-checker) 13 | spelling_exclusion_path = ./exclusion.dic 14 | 15 | [*.cs] 16 | indent_size = 4 17 | charset = utf-8-bom 18 | end_of_line = unset 19 | 20 | # Solution files 21 | [*.{sln,slnx}] 22 | end_of_line = unset 23 | 24 | # MSBuild project files 25 | [*.{csproj,props,targets}] 26 | end_of_line = unset 27 | 28 | # Xml config files 29 | [*.{ruleset,config,nuspec,resx,runsettings,DotSettings}] 30 | end_of_line = unset 31 | 32 | [*{_AssemblyInfo.cs,.notsupported.cs}] 33 | generated_code = true 34 | 35 | # C# code style settings 36 | [*.{cs}] 37 | dotnet_diagnostic.IDE0044.severity = none # IDE0044: Make field readonly 38 | 39 | # https://stackoverflow.com/questions/79195382/how-to-disable-fading-unused-methods-in-visual-studio-2022-17-12-0 40 | dotnet_diagnostic.IDE0051.severity = none # IDE0051: Remove unused private member 41 | dotnet_diagnostic.IDE0130.severity = none # IDE0130: Namespace does not match folder structure 42 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | # ref: https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot 2 | version: 2 3 | updates: 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" # Check for updates to GitHub Actions every week 8 | ignore: 9 | # I just want update action when major/minor version is updated. patch updates are too noisy. 10 | - dependency-name: '*' 11 | update-types: 12 | - version-update:semver-patch 13 | -------------------------------------------------------------------------------- /.github/workflows/build-debug.yaml: -------------------------------------------------------------------------------- 1 | name: Build-Debug 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | pull_request: 8 | branches: 9 | - "master" 10 | 11 | jobs: 12 | build-dotnet: 13 | permissions: 14 | contents: read 15 | runs-on: ubuntu-24.04 16 | timeout-minutes: 15 17 | steps: 18 | - uses: Cysharp/Actions/.github/actions/checkout@main 19 | - uses: Cysharp/Actions/.github/actions/setup-dotnet@main 20 | with: 21 | dotnet-version: | 22 | 9.0.x 23 | - run: dotnet build -c Debug 24 | - run: dotnet test -c Debug --no-build 25 | -------------------------------------------------------------------------------- /.github/workflows/build-release.yaml: -------------------------------------------------------------------------------- 1 | name: Build-Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | tag: 7 | description: "tag: git tag you want create. (sample 1.0.0)" 8 | required: true 9 | dry-run: 10 | description: "dry-run: true will never create relase/nuget." 11 | required: true 12 | default: false 13 | type: boolean 14 | 15 | jobs: 16 | build-dotnet: 17 | permissions: 18 | contents: read 19 | runs-on: ubuntu-24.04 20 | timeout-minutes: 10 21 | steps: 22 | - uses: Cysharp/Actions/.github/actions/checkout@main 23 | - uses: Cysharp/Actions/.github/actions/setup-dotnet@main 24 | # pack nuget 25 | - run: dotnet build -c Release -p:Version=${{ inputs.tag }} 26 | - run: dotnet test -c Release --no-build 27 | - run: dotnet pack -c Release --no-build -p:Version=${{ inputs.tag }} -o ./publish 28 | - uses: Cysharp/Actions/.github/actions/upload-artifact@main 29 | with: 30 | name: nuget 31 | path: ./publish 32 | retention-days: 1 33 | 34 | # release 35 | create-release: 36 | needs: [build-dotnet] 37 | permissions: 38 | contents: write 39 | uses: Cysharp/Actions/.github/workflows/create-release.yaml@main 40 | with: 41 | commit-id: ${{ github.sha }} 42 | dry-run: ${{ inputs.dry-run }} 43 | tag: ${{ inputs.tag }} 44 | nuget-push: true 45 | secrets: inherit 46 | -------------------------------------------------------------------------------- /.github/workflows/prevent-github-change.yaml: -------------------------------------------------------------------------------- 1 | name: Prevent github change 2 | on: 3 | pull_request: 4 | paths: 5 | - ".github/**/*.yaml" 6 | - ".github/**/*.yml" 7 | 8 | jobs: 9 | detect: 10 | permissions: 11 | contents: read 12 | uses: Cysharp/Actions/.github/workflows/prevent-github-change.yaml@main 13 | -------------------------------------------------------------------------------- /.github/workflows/stale.yaml: -------------------------------------------------------------------------------- 1 | name: "Close stale issues" 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | 8 | jobs: 9 | stale: 10 | permissions: 11 | contents: read 12 | pull-requests: write 13 | issues: write 14 | uses: Cysharp/Actions/.github/workflows/stale-issue.yaml@main 15 | -------------------------------------------------------------------------------- /.github/workflows/toc.yaml: -------------------------------------------------------------------------------- 1 | name: TOC Generator 2 | 3 | on: 4 | push: 5 | paths: 6 | - 'README.md' 7 | 8 | jobs: 9 | toc: 10 | permissions: 11 | contents: write 12 | uses: Cysharp/Actions/.github/workflows/toc-generator.yaml@main 13 | with: 14 | TOC_TITLE: "## Table of Contents" 15 | secrets: inherit 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | 5 | # mstest test results 6 | TestResults 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Rr]elease/ 19 | x64/ 20 | *_i.c 21 | *_p.c 22 | *.ilk 23 | *.obj 24 | *.pch 25 | *.pdb 26 | *.pgc 27 | *.pgd 28 | *.rsp 29 | *.sbr 30 | *.tlb 31 | *.tli 32 | *.tlh 33 | *.tmp 34 | *.log 35 | *.vspscc 36 | *.vssscc 37 | .builds 38 | 39 | # Visual C++ cache files 40 | ipch/ 41 | *.aps 42 | *.ncb 43 | *.opensdf 44 | *.sdf 45 | 46 | # Visual Studio profiler 47 | *.psess 48 | *.vsp 49 | *.vspx 50 | 51 | # Guidance Automation Toolkit 52 | *.gpState 53 | 54 | # ReSharper is a .NET coding add-in 55 | _ReSharper* 56 | 57 | # NCrunch 58 | *.ncrunch* 59 | .*crunch*.local.xml 60 | 61 | # Installshield output folder 62 | [Ee]xpress 63 | 64 | # DocProject is a documentation generator add-in 65 | DocProject/buildhelp/ 66 | DocProject/Help/*.HxT 67 | DocProject/Help/*.HxC 68 | DocProject/Help/*.hhc 69 | DocProject/Help/*.hhk 70 | DocProject/Help/*.hhp 71 | DocProject/Help/Html2 72 | DocProject/Help/html 73 | 74 | # Click-Once directory 75 | publish 76 | 77 | # Publish Web Output 78 | *.Publish.xml 79 | 80 | # NuGet Packages Directory 81 | packages 82 | 83 | # Windows Azure Build Output 84 | csx 85 | *.build.csdef 86 | 87 | # Windows Store app package directory 88 | AppPackages/ 89 | 90 | # Others 91 | [Bb]in 92 | [Oo]bj 93 | sql 94 | TestResults 95 | [Tt]est[Rr]esult* 96 | *.Cache 97 | ClientBin 98 | [Ss]tyle[Cc]op.* 99 | ~$* 100 | *.dbmdl 101 | Generated_Code #added for RIA/Silverlight projects 102 | 103 | # Backup & report files from converting an old project file to a newer 104 | # Visual Studio version. Backup files are not needed, because we have git ;-) 105 | _UpgradeReport_Files/ 106 | Backup*/ 107 | UpgradeLog*.XML 108 | .vs/config/applicationhost.config 109 | .vs/restore.dg 110 | 111 | # OTHER 112 | nuget/tools/* 113 | *.nupkg 114 | 115 | .vs 116 | .vsconfig 117 | 118 | # Unity 119 | src/MasterMemory.Unity/bin/* 120 | src/MasterMemory.Unity/Library/* 121 | src/MasterMemory.Unity/obj/* 122 | src/MasterMemory.Unity/Temp/* 123 | src/MasterMemory.Unity/[Uu]ser[Ss]ettings/ 124 | src/MasterMemory.Unity/*.sln 125 | src/MasterMemory.Unity/*.csproj 126 | src/MasterMemory.Unity/*.unitypackage 127 | !src/MasterMemory.Unity/Packages/ 128 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Yoshifumi Kawai / Cysharp, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /sandbox/Benchmark/Benchmark.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net6.0 6 | false 7 | 1701;1702;NU1904 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Analyzer 25 | false 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /sandbox/Benchmark/Utils/Helper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using MessagePack; 8 | using LiteDB; 9 | using MasterMemory; 10 | 11 | namespace TestPerfLiteDB 12 | { 13 | [MemoryTable("TestDoc"), MessagePackObject(true)] 14 | public class TestDoc 15 | { 16 | [PrimaryKey] 17 | public int id { get; set; } 18 | public string name { get; set; } 19 | public string lorem { get; set; } 20 | 21 | public TestDoc() 22 | { 23 | 24 | } 25 | 26 | public TestDoc(int id, string name, string lorem) 27 | { 28 | this.id = id; 29 | this.name = name; 30 | this.lorem = lorem; 31 | } 32 | } 33 | 34 | public static class Helper 35 | { 36 | public static IEnumerable GetDocs(int count) 37 | { 38 | for (var i = 0; i < count; i++) 39 | { 40 | yield return new BsonDocument 41 | { 42 | { "_id", i }, 43 | { "name", Guid.NewGuid().ToString() }, 44 | { "lorem", LoremIpsum(3, 5, 2, 3, 3) } 45 | }; 46 | } 47 | } 48 | 49 | public static string LoremIpsum(int minWords, int maxWords, 50 | int minSentences, int maxSentences, 51 | int numParagraphs) 52 | { 53 | var words = new[] { "lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", 54 | "adipiscing", "elit", "sed", "diam", "nonummy", "nibh", "euismod", 55 | "tincidunt", "ut", "laoreet", "dolore", "magna", "aliquam", "erat" }; 56 | 57 | var rand = new Random(DateTime.Now.Millisecond); 58 | var numSentences = rand.Next(maxSentences - minSentences) + minSentences + 1; 59 | var numWords = rand.Next(maxWords - minWords) + minWords + 1; 60 | 61 | var result = new StringBuilder(); 62 | 63 | for (int p = 0; p < numParagraphs; p++) 64 | { 65 | for (int s = 0; s < numSentences; s++) 66 | { 67 | for (int w = 0; w < numWords; w++) 68 | { 69 | if (w > 0) { result.Append(" "); } 70 | result.Append(words[rand.Next(words.Length)]); 71 | } 72 | result.Append(". "); 73 | } 74 | result.AppendLine(); 75 | } 76 | 77 | return result.ToString(); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /sandbox/ConsoleApp/ConsoleApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net9.0 6 | false 7 | 8 | 9 | 10 | $(DefineConstants)TRACE; 11 | 12 | 13 | 14 | $(DefineConstants)TRACE; 15 | 16 | 17 | 18 | 19 | 20 | 21 | Analyzer 22 | false 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /sandbox/GeneratorSandbox/GeneratorSandbox.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | Analyzer 26 | false 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /sandbox/GeneratorSandbox/Program.cs: -------------------------------------------------------------------------------- 1 | using MasterMemory; 2 | using MessagePack; 3 | using GeneratorSandbox; 4 | using System.Runtime.CompilerServices; 5 | 6 | //[assembly: MasterMemoryGeneratorOptions( 7 | // Namespace = "Z", 8 | // IsReturnNullIfKeyNotFound = true, 9 | // PrefixClassName = "")] 10 | 11 | // to create database, use DatabaseBuilder and Append method. 12 | var builder = new DatabaseBuilder(); 13 | builder.Append(new Person[] 14 | { 15 | new (){ PersonId = 0, Age = 13, Gender = Gender.Male, Name = "Dana Terry" }, 16 | new (){ PersonId = 1, Age = 17, Gender = Gender.Male, Name = "Kirk Obrien" }, 17 | new (){ PersonId = 2, Age = 31, Gender = Gender.Male, Name = "Wm Banks" }, 18 | new (){ PersonId = 3, Age = 44, Gender = Gender.Male, Name = "Karl Benson" }, 19 | new (){ PersonId = 4, Age = 23, Gender = Gender.Male, Name = "Jared Holland" }, 20 | new (){ PersonId = 5, Age = 27, Gender = Gender.Female, Name = "Jeanne Phelps" }, 21 | new (){ PersonId = 6, Age = 25, Gender = Gender.Female, Name = "Willie Rose" }, 22 | new (){ PersonId = 7, Age = 11, Gender = Gender.Female, Name = "Shari Gutierrez" }, 23 | new (){ PersonId = 8, Age = 63, Gender = Gender.Female, Name = "Lori Wilson" }, 24 | new (){ PersonId = 9, Age = 34, Gender = Gender.Female, Name = "Lena Ramsey" }, 25 | }); 26 | 27 | // build database binary(you can also use `WriteToStream` for save to file). 28 | byte[] data = builder.Build(); 29 | 30 | 31 | var db = new MemoryDatabase(data); 32 | 33 | // .PersonTable.FindByPersonId is fully typed by code-generation. 34 | Person person = db.PersonTable.FindByPersonId(5); 35 | 36 | // Multiple key is also typed(***And * **), Return value is multiple if key is marked with `NonUnique`. 37 | RangeView result = db.PersonTable.FindByGenderAndAge((Gender.Female, 23)); 38 | 39 | // Get nearest value(choose lower(default) or higher). 40 | RangeView age1 = db.PersonTable.FindClosestByAge(31); 41 | 42 | // Get range(min-max inclusive). 43 | RangeView age2 = db.PersonTable.FindRangeByAge(20, 29); 44 | 45 | 46 | public enum Gender 47 | { 48 | Male, Female, Unknown 49 | } 50 | 51 | 52 | 53 | // table definition marked by MemoryTableAttribute. 54 | // database-table must be serializable by MessagePack-CSsharp 55 | [MemoryTable("person"), MessagePackObject(true)] 56 | public record Person 57 | { 58 | // index definition by attributes. 59 | [PrimaryKey] 60 | public required int PersonId { get; init; } 61 | 62 | // secondary index can add multiple(discriminated by index-number). 63 | [SecondaryKey(0), NonUnique] 64 | [SecondaryKey(1, keyOrder: 1), NonUnique] 65 | public required int Age { get; init; } 66 | 67 | [SecondaryKey(2), NonUnique] 68 | [SecondaryKey(1, keyOrder: 0), NonUnique] 69 | public required Gender Gender { get; init; } 70 | 71 | public required string Name { get; init; } 72 | } -------------------------------------------------------------------------------- /sandbox/PerfTest2/Engines/Dictionary_Test.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Collections.Immutable; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using TestPerfLiteDB; 10 | 11 | namespace TestPerfLiteDB 12 | { 13 | public class Dictionary_Test : ITest 14 | { 15 | private string _filename; 16 | private int _count; 17 | 18 | Dictionary dict; 19 | 20 | public int Count { get { return _count; } } 21 | public int FileLength { get { return (int)new FileInfo(_filename).Length; } } 22 | 23 | public Dictionary_Test(int count) 24 | { 25 | _count = count; 26 | _filename = "dict-" + Guid.NewGuid().ToString("n") + ".db"; 27 | dict = new Dictionary(); 28 | } 29 | 30 | public void Insert() 31 | { 32 | foreach (var doc in Helper.GetDocs(_count)) 33 | { 34 | var v = new TestDoc 35 | { 36 | id = doc["_id"].AsInt32, 37 | name = doc["name"].AsString, 38 | lorem = doc["lorem"].AsString 39 | }; 40 | 41 | dict.Add(v.id, v); 42 | } 43 | } 44 | 45 | public void Bulk() 46 | { 47 | 48 | } 49 | 50 | public void CreateIndex() 51 | { 52 | 53 | } 54 | 55 | public void Dispose() 56 | { 57 | 58 | } 59 | 60 | public void Prepare() 61 | { 62 | 63 | } 64 | 65 | public void Query() 66 | { 67 | for (var i = 0; i < _count; i++) 68 | { 69 | TestDoc d; 70 | dict.TryGetValue(i, out d); 71 | } 72 | } 73 | 74 | public void Update() 75 | { 76 | 77 | } 78 | } 79 | 80 | public class ConcurrentDictionary_Test : ITest 81 | { 82 | private string _filename; 83 | private int _count; 84 | 85 | ConcurrentDictionary dict; 86 | 87 | public int Count { get { return _count; } } 88 | public int FileLength { get { return (int)new FileInfo(_filename).Length; } } 89 | 90 | public ConcurrentDictionary_Test(int count) 91 | { 92 | _count = count; 93 | _filename = "concurrentdict-" + Guid.NewGuid().ToString("n") + ".db"; 94 | dict = new ConcurrentDictionary(); 95 | } 96 | 97 | public void Insert() 98 | { 99 | foreach (var doc in Helper.GetDocs(_count)) 100 | { 101 | var v = new TestDoc 102 | { 103 | id = doc["_id"].AsInt32, 104 | name = doc["name"].AsString, 105 | lorem = doc["lorem"].AsString 106 | }; 107 | 108 | dict.TryAdd(v.id, v); 109 | } 110 | } 111 | 112 | public void Bulk() 113 | { 114 | 115 | } 116 | 117 | public void CreateIndex() 118 | { 119 | 120 | } 121 | 122 | public void Dispose() 123 | { 124 | 125 | } 126 | 127 | public void Prepare() 128 | { 129 | 130 | } 131 | 132 | public void Query() 133 | { 134 | for (var i = 0; i < _count; i++) 135 | { 136 | TestDoc d; 137 | dict.TryGetValue(i, out d); 138 | } 139 | } 140 | 141 | public void Update() 142 | { 143 | 144 | } 145 | } 146 | 147 | public class ImmutableDictionary_Test : ITest 148 | { 149 | private string _filename; 150 | private int _count; 151 | 152 | ImmutableDictionary dict; 153 | 154 | public int Count { get { return _count; } } 155 | public int FileLength { get { return (int)new FileInfo(_filename).Length; } } 156 | 157 | public ImmutableDictionary_Test(int count) 158 | { 159 | _count = count; 160 | _filename = "immutabledict-" + Guid.NewGuid().ToString("n") + ".db"; 161 | //dict = new ImmutableDictionary(); 162 | } 163 | 164 | public void Insert() 165 | { 166 | var builder = ImmutableDictionary.CreateBuilder(); 167 | foreach (var doc in Helper.GetDocs(_count)) 168 | { 169 | var v = new TestDoc 170 | { 171 | id = doc["_id"].AsInt32, 172 | name = doc["name"].AsString, 173 | lorem = doc["lorem"].AsString 174 | }; 175 | 176 | builder.Add(v.id, v); 177 | } 178 | 179 | dict = builder.ToImmutableDictionary(); 180 | } 181 | 182 | public void Bulk() 183 | { 184 | 185 | } 186 | 187 | public void CreateIndex() 188 | { 189 | 190 | } 191 | 192 | public void Dispose() 193 | { 194 | 195 | } 196 | 197 | public void Prepare() 198 | { 199 | 200 | } 201 | 202 | public void Query() 203 | { 204 | for (var i = 0; i < _count; i++) 205 | { 206 | TestDoc d; 207 | dict.TryGetValue(i, out d); 208 | } 209 | } 210 | 211 | public void Update() 212 | { 213 | 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /sandbox/PerfTest2/Engines/ITest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.SQLite; 5 | using System.Diagnostics; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using LiteDB; 11 | 12 | namespace TestPerfLiteDB 13 | { 14 | public interface ITest : IDisposable 15 | { 16 | int Count { get; } 17 | int FileLength { get; } 18 | 19 | void Prepare(); 20 | void Insert(); 21 | void Bulk(); 22 | void Update(); 23 | void CreateIndex(); 24 | void Query(); 25 | //void Delete(); 26 | //void Drop(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sandbox/PerfTest2/Engines/LiteDB_Test.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.SQLite; 5 | using System.Diagnostics; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using LiteDB; 11 | 12 | namespace TestPerfLiteDB 13 | { 14 | public class LiteDB_Test : ITest 15 | { 16 | private string _filename; 17 | private LiteEngine _db; 18 | private int _count; 19 | 20 | public int Count { get { return _count; } } 21 | public int FileLength { get { return (int)new FileInfo(_filename).Length; } } 22 | 23 | public LiteDB_Test(int count, string password, LiteDB.FileOptions options) 24 | { 25 | _count = count; 26 | _filename = "dblite-" + Guid.NewGuid().ToString("n") + ".db"; 27 | 28 | var disk = new FileDiskService(_filename, options); 29 | 30 | _db = new LiteEngine(disk, password); 31 | } 32 | 33 | public LiteDB_Test(int count) 34 | { 35 | _count = count; 36 | _filename = "dblite-" + Guid.NewGuid().ToString("n") + ".db"; 37 | 38 | var ms = new MemoryStream(); 39 | var disk = new LiteDB.StreamDiskService(ms); 40 | //var disk = new FileDiskService(_filename, options); 41 | 42 | _db = new LiteEngine(disk); 43 | } 44 | 45 | public void Prepare() 46 | { 47 | } 48 | 49 | public void Insert() 50 | { 51 | foreach (var doc in Helper.GetDocs(_count)) 52 | { 53 | _db.Insert("col", doc); 54 | } 55 | } 56 | 57 | public void Bulk() 58 | { 59 | _db.Insert("col_bulk", Helper.GetDocs(_count)); 60 | } 61 | 62 | public void Update() 63 | { 64 | foreach (var doc in Helper.GetDocs(_count)) 65 | { 66 | _db.Update("col", doc); 67 | } 68 | } 69 | 70 | public void CreateIndex() 71 | { 72 | _db.EnsureIndex("col", "name", false); 73 | } 74 | 75 | public void Query() 76 | { 77 | for (var i = 0; i < _count; i++) 78 | { 79 | _db.Find("col", LiteDB.Query.EQ("_id", i)).Single(); 80 | } 81 | } 82 | 83 | public void Delete() 84 | { 85 | _db.Delete("col", LiteDB.Query.All()); 86 | } 87 | 88 | public void Drop() 89 | { 90 | _db.DropCollection("col_bulk"); 91 | } 92 | 93 | public void Dispose() 94 | { 95 | _db.Dispose(); 96 | File.Delete(_filename); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /sandbox/PerfTest2/Engines/MasterMemory_Test.cs: -------------------------------------------------------------------------------- 1 | using MasterMemory; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace TestPerfLiteDB 10 | { 11 | public class MasterMemory_Test : ITest 12 | { 13 | private string _filename; 14 | private int _count; 15 | 16 | MemoryDatabase database; 17 | 18 | public int Count { get { return _count; } } 19 | public int FileLength { get { return (int)new FileInfo(_filename).Length; } } 20 | 21 | public MasterMemory_Test(int count) 22 | { 23 | _count = count; 24 | _filename = "mastermemorydatabase-" + Guid.NewGuid().ToString("n") + ".db"; 25 | 26 | } 27 | 28 | public IEnumerable MakeDoc() 29 | { 30 | foreach (var doc in Helper.GetDocs(_count)) 31 | { 32 | var v = new TestDoc 33 | { 34 | id = doc["_id"].AsInt32, 35 | name = doc["name"].AsString, 36 | lorem = doc["lorem"].AsString 37 | }; 38 | 39 | yield return v; 40 | } 41 | } 42 | 43 | public void Insert() 44 | { 45 | var builder = new DatabaseBuilder(); 46 | builder.Append(MakeDoc()); 47 | var saved = builder.Build(); 48 | File.WriteAllBytes(_filename, saved); 49 | database = new MemoryDatabase(saved); 50 | } 51 | 52 | public void Bulk() 53 | { 54 | 55 | } 56 | 57 | public void CreateIndex() 58 | { 59 | 60 | } 61 | 62 | public void Dispose() 63 | { 64 | 65 | } 66 | 67 | public void Prepare() 68 | { 69 | 70 | } 71 | 72 | public void Query() 73 | { 74 | for (var i = 0; i < _count; i++) 75 | { 76 | //TestDoc d; 77 | database.TestDocTable.FindByid(i); 78 | } 79 | } 80 | 81 | public void Update() 82 | { 83 | 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /sandbox/PerfTest2/Engines/RavenDB_Test.cs: -------------------------------------------------------------------------------- 1 | //using Raven.Client; 2 | //using Raven.Client.Embedded; 3 | //using Raven.Client.Indexes; 4 | //using System; 5 | //using System.Collections.Generic; 6 | //using System.IO; 7 | //using System.Linq; 8 | //using System.Text; 9 | //using System.Threading.Tasks; 10 | 11 | //namespace TestPerfLiteDB 12 | //{ 13 | // public class TestDocCreation : AbstractIndexCreationTask 14 | // { 15 | // public TestDocCreation() 16 | // { 17 | // Map = xs => xs.Select(x => new { x.id }); 18 | // } 19 | // } 20 | 21 | // public class RavenDB_Test : ITest 22 | // { 23 | // private string _filename; 24 | // private int _count; 25 | // private bool isinmemory; 26 | 27 | // IDocumentStore store; 28 | // public int Count { get { return _count; } } 29 | // public int FileLength { get { return (int)new FileInfo(_filename).Length; } } 30 | 31 | // public RavenDB_Test(int count, bool isinmemory) 32 | // { 33 | // _count = count; 34 | // _filename = "ravendb-" + Guid.NewGuid().ToString("n") + ".db"; 35 | // this.isinmemory = isinmemory; 36 | // } 37 | 38 | // public void Bulk() 39 | // { 40 | // if (isinmemory) 41 | // { 42 | 43 | 44 | // } 45 | 46 | // //using (var store = new EmbeddableDocumentStore { RunInMemory = true }.Initialize()) 47 | // //{ 48 | // // using (var bulk = store.BulkInsert()) 49 | // // { 50 | // // bulk.Store(new MyClass { Id = 9999, MyProperty = 1000 }); 51 | // // } 52 | 53 | // // var session = store.OpenSession(); 54 | 55 | // // store.ExecuteIndex(new MyClassIndex()); 56 | 57 | // // var huga = session.Load("MyClasses/9999"); 58 | // //} 59 | // } 60 | 61 | // public void CreateIndex() 62 | // { 63 | // store.ExecuteIndex(new TestDocCreation()); 64 | // } 65 | 66 | // public void Dispose() 67 | // { 68 | // store.Dispose(); 69 | // } 70 | // IEnumerable MakeDoc() 71 | // { 72 | // foreach (var doc in Helper.GetDocs(_count)) 73 | // { 74 | // var v = new TestDoc 75 | // { 76 | // id = doc["_id"].AsInt32, 77 | // name = doc["name"].AsString, 78 | // lorem = doc["lorem"].AsString 79 | // }; 80 | 81 | // yield return v; 82 | // } 83 | // } 84 | 85 | 86 | // public void Insert() 87 | // { 88 | 89 | // using (var bulk = store.BulkInsert()) 90 | // { 91 | // foreach (var item in MakeDoc()) 92 | // { 93 | // bulk.Store(item); 94 | // } 95 | // } 96 | 97 | // } 98 | 99 | // public void Prepare() 100 | // { 101 | // if (isinmemory) 102 | // { 103 | // store = new EmbeddableDocumentStore { RunInMemory = true }.Initialize(); 104 | // } 105 | // else 106 | // { 107 | // // store = new EmbeddableDocumentStore { RunInMemory = true }.Initialize(); 108 | // } 109 | // } 110 | 111 | // public void Query() 112 | // { 113 | // for (var i = 0; i < _count; i++) 114 | // { 115 | // using (var session = store.OpenSession()) 116 | // { 117 | // session.Load("TestDoc/" + i); 118 | // } 119 | // } 120 | // } 121 | 122 | // public void Update() 123 | // { 124 | 125 | // } 126 | // } 127 | //} 128 | -------------------------------------------------------------------------------- /sandbox/PerfTest2/Engines/SQLite_Test.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.SQLite; 5 | using System.Diagnostics; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace TestPerfLiteDB 12 | { 13 | public class SQLite_Test : ITest 14 | { 15 | private string _filename; 16 | private SQLiteConnection _db; 17 | private int _count; 18 | 19 | public int Count { get { return _count; } } 20 | public int FileLength { get { return (int)new FileInfo(_filename).Length; } } 21 | 22 | public SQLite_Test(int count, string password, bool journal, bool memory = false) 23 | { 24 | _count = count; 25 | _filename = "sqlite-" + Guid.NewGuid().ToString("n") + ".db"; 26 | 27 | if (memory) 28 | { 29 | var cs = "Data Source=:memory:;New=True;"; 30 | _db = new SQLiteConnection(cs); 31 | } 32 | else 33 | { 34 | var cs = "Data Source=" + _filename; 35 | if (password != null) cs += "; Password=" + password; 36 | if (journal == false) cs += "; Journal Mode=Off"; 37 | _db = new SQLiteConnection(cs); 38 | } 39 | } 40 | 41 | public void Prepare() 42 | { 43 | _db.Open(); 44 | 45 | var table = new SQLiteCommand("CREATE TABLE col (id INTEGER NOT NULL PRIMARY KEY, name TEXT, lorem TEXT)", _db); 46 | table.ExecuteNonQuery(); 47 | 48 | var table2 = new SQLiteCommand("CREATE TABLE col_bulk (id INTEGER NOT NULL PRIMARY KEY, name TEXT, lorem TEXT)", _db); 49 | table2.ExecuteNonQuery(); 50 | } 51 | 52 | public void Insert() 53 | { 54 | // standard insert is slow, mod same as Bulk 55 | using (var trans = _db.BeginTransaction()) 56 | { 57 | var cmd = new SQLiteCommand("INSERT INTO col (id, name, lorem) VALUES (@id, @name, @lorem)", _db); 58 | 59 | cmd.Parameters.Add(new SQLiteParameter("id", DbType.Int32)); 60 | cmd.Parameters.Add(new SQLiteParameter("name", DbType.String)); 61 | cmd.Parameters.Add(new SQLiteParameter("lorem", DbType.String)); 62 | 63 | foreach (var doc in Helper.GetDocs(_count)) 64 | { 65 | cmd.Parameters["id"].Value = doc["_id"].AsInt32; 66 | cmd.Parameters["name"].Value = doc["name"].AsString; 67 | cmd.Parameters["lorem"].Value = doc["lorem"].AsString; 68 | 69 | cmd.ExecuteNonQuery(); 70 | } 71 | 72 | trans.Commit(); 73 | } 74 | } 75 | 76 | public void Bulk() 77 | { 78 | using (var trans = _db.BeginTransaction()) 79 | { 80 | var cmd = new SQLiteCommand("INSERT INTO col_bulk (id, name, lorem) VALUES (@id, @name, @lorem)", _db); 81 | 82 | cmd.Parameters.Add(new SQLiteParameter("id", DbType.Int32)); 83 | cmd.Parameters.Add(new SQLiteParameter("name", DbType.String)); 84 | cmd.Parameters.Add(new SQLiteParameter("lorem", DbType.String)); 85 | 86 | foreach (var doc in Helper.GetDocs(_count)) 87 | { 88 | cmd.Parameters["id"].Value = doc["_id"].AsInt32; 89 | cmd.Parameters["name"].Value = doc["name"].AsString; 90 | cmd.Parameters["lorem"].Value = doc["lorem"].AsString; 91 | 92 | cmd.ExecuteNonQuery(); 93 | } 94 | 95 | trans.Commit(); 96 | } 97 | } 98 | 99 | public void Update() 100 | { 101 | var cmd = new SQLiteCommand("UPDATE col SET name = @name, lorem = @lorem WHERE id = @id", _db); 102 | 103 | cmd.Parameters.Add(new SQLiteParameter("id", DbType.Int32)); 104 | cmd.Parameters.Add(new SQLiteParameter("name", DbType.String)); 105 | cmd.Parameters.Add(new SQLiteParameter("lorem", DbType.String)); 106 | 107 | foreach (var doc in Helper.GetDocs(_count)) 108 | { 109 | cmd.Parameters["id"].Value = doc["_id"].AsInt32; 110 | cmd.Parameters["name"].Value = doc["name"].AsString; 111 | cmd.Parameters["lorem"].Value = doc["lorem"].AsString; 112 | 113 | cmd.ExecuteNonQuery(); 114 | } 115 | } 116 | 117 | public void CreateIndex() 118 | { 119 | var cmd = new SQLiteCommand("CREATE INDEX idx1 ON col (name)", _db); 120 | 121 | cmd.ExecuteNonQuery(); 122 | } 123 | 124 | public void Query() 125 | { 126 | var cmd = new SQLiteCommand("SELECT * FROM col WHERE id = @id", _db); 127 | 128 | cmd.Parameters.Add(new SQLiteParameter("id", DbType.Int32)); 129 | 130 | for (var i = 0; i < _count; i++) 131 | { 132 | cmd.Parameters["id"].Value = i; 133 | 134 | var r = cmd.ExecuteReader(); 135 | 136 | r.Read(); 137 | 138 | var name = r.GetString(1); 139 | var lorem = r.GetString(2); 140 | 141 | r.Close(); 142 | } 143 | } 144 | 145 | public void Delete() 146 | { 147 | var cmd = new SQLiteCommand("DELETE FROM col", _db); 148 | 149 | cmd.ExecuteNonQuery(); 150 | } 151 | 152 | public void Drop() 153 | { 154 | var cmd = new SQLiteCommand("DROP TABLE col_bulk", _db); 155 | 156 | cmd.ExecuteNonQuery(); 157 | } 158 | 159 | public void Dispose() 160 | { 161 | _db.Dispose(); 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /sandbox/PerfTest2/Generated/DatabaseBuilder.cs: -------------------------------------------------------------------------------- 1 | // 2 | using LiteDB; 3 | using MasterMemory; 4 | using MessagePack; 5 | using System.Collections.Generic; 6 | using System.Data.SQLite; 7 | using System.Diagnostics; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System; 12 | using TestPerfLiteDB; 13 | using TestPerfLiteDB.Tables; 14 | 15 | namespace TestPerfLiteDB 16 | { 17 | public sealed class DatabaseBuilder : DatabaseBuilderBase 18 | { 19 | public DatabaseBuilder() : this(null) { } 20 | public DatabaseBuilder(MessagePack.IFormatterResolver resolver) : base(resolver) { } 21 | 22 | public DatabaseBuilder Append(System.Collections.Generic.IEnumerable dataSource) 23 | { 24 | AppendCore(dataSource, x => x.id, System.Collections.Generic.Comparer.Default); 25 | return this; 26 | } 27 | 28 | } 29 | } -------------------------------------------------------------------------------- /sandbox/PerfTest2/Generated/ImmutableBuilder.cs: -------------------------------------------------------------------------------- 1 | // 2 | using LiteDB; 3 | using MasterMemory; 4 | using MessagePack; 5 | using System.Collections.Generic; 6 | using System.Data.SQLite; 7 | using System.Diagnostics; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System; 12 | using TestPerfLiteDB; 13 | using TestPerfLiteDB.Tables; 14 | 15 | namespace TestPerfLiteDB 16 | { 17 | public sealed class ImmutableBuilder : ImmutableBuilderBase 18 | { 19 | MemoryDatabase memory; 20 | 21 | public ImmutableBuilder(MemoryDatabase memory) 22 | { 23 | this.memory = memory; 24 | } 25 | 26 | public MemoryDatabase Build() 27 | { 28 | return memory; 29 | } 30 | 31 | public void ReplaceAll(System.Collections.Generic.IList data) 32 | { 33 | var newData = CloneAndSortBy(data, x => x.id, System.Collections.Generic.Comparer.Default); 34 | var table = new TestDocTable(newData); 35 | memory = new MemoryDatabase( 36 | table 37 | 38 | ); 39 | } 40 | 41 | public void RemoveTestDoc(int[] keys) 42 | { 43 | var data = RemoveCore(memory.TestDocTable.GetRawDataUnsafe(), keys, x => x.id, System.Collections.Generic.Comparer.Default); 44 | var newData = CloneAndSortBy(data, x => x.id, System.Collections.Generic.Comparer.Default); 45 | var table = new TestDocTable(newData); 46 | memory = new MemoryDatabase( 47 | table 48 | 49 | ); 50 | } 51 | 52 | public void Diff(TestDoc[] addOrReplaceData) 53 | { 54 | var data = DiffCore(memory.TestDocTable.GetRawDataUnsafe(), addOrReplaceData, x => x.id, System.Collections.Generic.Comparer.Default); 55 | var newData = CloneAndSortBy(data, x => x.id, System.Collections.Generic.Comparer.Default); 56 | var table = new TestDocTable(newData); 57 | memory = new MemoryDatabase( 58 | table 59 | 60 | ); 61 | } 62 | 63 | } 64 | } -------------------------------------------------------------------------------- /sandbox/PerfTest2/Generated/MasterMemoryResolver.cs: -------------------------------------------------------------------------------- 1 | // 2 | using LiteDB; 3 | using MasterMemory; 4 | using MessagePack; 5 | using System.Collections.Generic; 6 | using System.Data.SQLite; 7 | using System.Diagnostics; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System; 12 | using TestPerfLiteDB; 13 | using TestPerfLiteDB.Tables; 14 | 15 | namespace TestPerfLiteDB 16 | { 17 | public class MasterMemoryResolver : global::MessagePack.IFormatterResolver 18 | { 19 | public static readonly global::MessagePack.IFormatterResolver Instance = new MasterMemoryResolver(); 20 | 21 | MasterMemoryResolver() 22 | { 23 | 24 | } 25 | 26 | public global::MessagePack.Formatters.IMessagePackFormatter GetFormatter() 27 | { 28 | return FormatterCache.formatter; 29 | } 30 | 31 | static class FormatterCache 32 | { 33 | public static readonly global::MessagePack.Formatters.IMessagePackFormatter formatter; 34 | 35 | static FormatterCache() 36 | { 37 | var f = MasterMemoryResolverGetFormatterHelper.GetFormatter(typeof(T)); 38 | if (f != null) 39 | { 40 | formatter = (global::MessagePack.Formatters.IMessagePackFormatter)f; 41 | } 42 | } 43 | } 44 | } 45 | 46 | internal static class MasterMemoryResolverGetFormatterHelper 47 | { 48 | static readonly global::System.Collections.Generic.Dictionary lookup; 49 | 50 | static MasterMemoryResolverGetFormatterHelper() 51 | { 52 | lookup = new global::System.Collections.Generic.Dictionary(1) 53 | { 54 | {typeof(TestDoc[]), 0 }, 55 | }; 56 | } 57 | 58 | internal static object GetFormatter(Type t) 59 | { 60 | int key; 61 | if (!lookup.TryGetValue(t, out key)) return null; 62 | 63 | switch (key) 64 | { 65 | case 0: return new MessagePack.Formatters.ArrayFormatter(); 66 | default: return null; 67 | } 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /sandbox/PerfTest2/Generated/MemoryDatabase.cs: -------------------------------------------------------------------------------- 1 | // 2 | using LiteDB; 3 | using MasterMemory; 4 | using MessagePack; 5 | using System.Collections.Generic; 6 | using System.Data.SQLite; 7 | using System.Diagnostics; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System; 12 | using TestPerfLiteDB; 13 | using TestPerfLiteDB.Tables; 14 | 15 | namespace TestPerfLiteDB 16 | { 17 | public sealed class MemoryDatabase : MemoryDatabaseBase 18 | { 19 | public TestDocTable TestDocTable { get; private set; } 20 | 21 | public MemoryDatabase( 22 | TestDocTable TestDocTable 23 | ) 24 | { 25 | this.TestDocTable = TestDocTable; 26 | } 27 | 28 | public MemoryDatabase(byte[] databaseBinary, bool internString = true, MessagePack.IFormatterResolver formatterResolver = null, int maxDegreeOfParallelism = 1) 29 | : base(databaseBinary, internString, formatterResolver, maxDegreeOfParallelism) 30 | { 31 | } 32 | 33 | protected override void Init(Dictionary header, System.ReadOnlyMemory databaseBinary, MessagePack.MessagePackSerializerOptions options, int maxDegreeOfParallelism) 34 | { 35 | this.TestDocTable = ExtractTableData(header, databaseBinary, options, xs => new TestDocTable(xs)); 36 | } 37 | 38 | public ImmutableBuilder ToImmutableBuilder() 39 | { 40 | return new ImmutableBuilder(this); 41 | } 42 | 43 | public DatabaseBuilder ToDatabaseBuilder() 44 | { 45 | var builder = new DatabaseBuilder(); 46 | builder.Append(this.TestDocTable.GetRawDataUnsafe()); 47 | return builder; 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /sandbox/PerfTest2/Generated/Tables/TestDocTable.cs: -------------------------------------------------------------------------------- 1 | // 2 | using LiteDB; 3 | using MasterMemory; 4 | using MessagePack; 5 | using System.Collections.Generic; 6 | using System.Data.SQLite; 7 | using System.Diagnostics; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System; 12 | using TestPerfLiteDB; 13 | 14 | namespace TestPerfLiteDB.Tables 15 | { 16 | public sealed partial class TestDocTable : TableBase 17 | { 18 | readonly Func primaryIndexSelector; 19 | 20 | 21 | public TestDocTable(TestDoc[] sortedData) 22 | : base(sortedData) 23 | { 24 | this.primaryIndexSelector = x => x.id; 25 | } 26 | 27 | 28 | [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] 29 | public TestDoc FindByid(int key) 30 | { 31 | var lo = 0; 32 | var hi = data.Length - 1; 33 | while (lo <= hi) 34 | { 35 | var mid = (int)(((uint)hi + (uint)lo) >> 1); 36 | var selected = data[mid].id; 37 | var found = (selected < key) ? -1 : (selected > key) ? 1 : 0; 38 | if (found == 0) { return data[mid]; } 39 | if (found < 0) { lo = mid + 1; } 40 | else { hi = mid - 1; } 41 | } 42 | return default; 43 | } 44 | 45 | public TestDoc FindClosestByid(int key, bool selectLower = true) 46 | { 47 | return FindUniqueClosestCore(data, primaryIndexSelector, System.Collections.Generic.Comparer.Default, key, selectLower); 48 | } 49 | 50 | public RangeView FindRangeByid(int min, int max, bool ascendant = true) 51 | { 52 | return FindUniqueRangeCore(data, primaryIndexSelector, System.Collections.Generic.Comparer.Default, min, max, ascendant); 53 | } 54 | 55 | } 56 | } -------------------------------------------------------------------------------- /sandbox/PerfTest2/PerfTest2.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net9.0 6 | false 7 | 1701;1702;NU1904 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Analyzer 19 | false 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /sandbox/PerfTest2/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.SQLite; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using LiteDB; 9 | using System.IO; 10 | 11 | // based test code -> https://github.com/mbdavid/LiteDB-Perf 12 | 13 | namespace TestPerfLiteDB 14 | { 15 | class Program 16 | { 17 | static void Main(string[] args) 18 | { 19 | RunTest("LiteDB: default", new LiteDB_Test(5000, null, new LiteDB.FileOptions { Journal = true, FileMode = LiteDB.FileMode.Shared })); 20 | RunTest("LiteDB: encrypted", new LiteDB_Test(5000, "mypass", new LiteDB.FileOptions { Journal = true, FileMode = LiteDB.FileMode.Shared })); 21 | RunTest("LiteDB: exclusive no journal", new LiteDB_Test(5000, null, new LiteDB.FileOptions { Journal = false, FileMode = LiteDB.FileMode.Exclusive })); 22 | RunTest("LiteDB: in-memory", new LiteDB_Test(5000)); 23 | 24 | RunTest("SQLite: default", new SQLite_Test(5000, null, true)); 25 | //RunTest("SQLite: encrypted", new SQLite_Test(5000, "mypass", true)); 26 | //RunTest("SQLite: no journal", new SQLite_Test(5000, null, false)); 27 | RunTest("SQLite: in-memory", new SQLite_Test(5000, null, false, true)); 28 | 29 | 30 | 31 | RunTest("Dictionary", new Dictionary_Test(5000)); 32 | RunTest("ConcurrentDictionary", new ConcurrentDictionary_Test(5000)); 33 | RunTest("ImmutableDictionary", new ImmutableDictionary_Test(5000)); 34 | 35 | RunTest("MasterMemory", new MasterMemory_Test(5000)); 36 | 37 | Console.ReadKey(); 38 | 39 | // RunTest("RavenDB: in-memory", new RavenDB_Test(5000, true)); 40 | } 41 | 42 | static void RunTest(string name, ITest test) 43 | { 44 | var title = name + " - " + test.Count + " records"; 45 | Console.WriteLine(title); 46 | Console.WriteLine("=".PadLeft(title.Length, '=')); 47 | 48 | test.Prepare(); 49 | 50 | test.Run("Insert", test.Insert, true); 51 | test.Run("Bulk", test.Bulk, true); 52 | test.Run("CreateIndex", test.CreateIndex, true); 53 | test.Run("Query", test.Query, false); 54 | test.Run("Query", test.Query, false); 55 | test.Run("Query", test.Query, false); 56 | test.Run("Query", test.Query, false); 57 | 58 | try 59 | { 60 | Console.WriteLine("FileLength : " + Math.Round((double)test.FileLength / (double)1024, 2).ToString().PadLeft(5, ' ') + " kb"); 61 | } 62 | catch (System.IO.FileNotFoundException) 63 | { 64 | } 65 | 66 | test.Dispose(); 67 | 68 | Console.WriteLine(); 69 | 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /sandbox/PerfTest2/Utils/Helper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.SQLite; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using LiteDB; 9 | using MasterMemory; 10 | using MessagePack; 11 | 12 | namespace TestPerfLiteDB 13 | { 14 | [MemoryTable("TestDoc"), MessagePackObject(true)] 15 | public class TestDoc 16 | { 17 | [PrimaryKey] 18 | public int id { get; set; } 19 | public string name { get; set; } 20 | public string lorem { get; set; } 21 | } 22 | 23 | static class Helper 24 | { 25 | public static void Run(this ITest test, string name, Action action, bool dryrun) 26 | { 27 | var sw = new Stopwatch(); 28 | 29 | System.GC.Collect(2, GCCollectionMode.Forced, blocking: true); 30 | sw.Start(); 31 | action(); 32 | sw.Stop(); 33 | 34 | var time = sw.ElapsedMilliseconds.ToString().PadLeft(5, ' '); 35 | var seg = Math.Round(test.Count / sw.Elapsed.TotalSeconds).ToString().PadLeft(8, ' '); 36 | 37 | if (!dryrun) 38 | { 39 | Console.WriteLine(name.PadRight(15, ' ') + ": " + 40 | time + " ms - " + 41 | seg + " records/second"); 42 | } 43 | } 44 | 45 | public static IEnumerable GetDocs(int count) 46 | { 47 | for (var i = 0; i < count; i++) 48 | { 49 | yield return new BsonDocument 50 | { 51 | { "_id", i }, 52 | { "name", Guid.NewGuid().ToString() }, 53 | { "lorem", LoremIpsum(3, 5, 2, 3, 3) } 54 | }; 55 | } 56 | } 57 | 58 | public static string LoremIpsum(int minWords, int maxWords, 59 | int minSentences, int maxSentences, 60 | int numParagraphs) 61 | { 62 | var words = new[] { "lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", 63 | "adipiscing", "elit", "sed", "diam", "nonummy", "nibh", "euismod", 64 | "tincidunt", "ut", "laoreet", "dolore", "magna", "aliquam", "erat" }; 65 | 66 | var rand = new Random(DateTime.Now.Millisecond); 67 | var numSentences = rand.Next(maxSentences - minSentences) + minSentences + 1; 68 | var numWords = rand.Next(maxWords - minWords) + minWords + 1; 69 | 70 | var result = new StringBuilder(); 71 | 72 | for (int p = 0; p < numParagraphs; p++) 73 | { 74 | for (int s = 0; s < numSentences; s++) 75 | { 76 | for (int w = 0; w < numWords; w++) 77 | { 78 | if (w > 0) { result.Append(" "); } 79 | result.Append(words[rand.Next(words.Length)]); 80 | } 81 | result.Append(". "); 82 | } 83 | result.AppendLine(); 84 | } 85 | 86 | return result.ToString(); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/MasterMemory.Annotations/Attributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MasterMemory 4 | { 5 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 6 | public class MemoryTableAttribute : Attribute 7 | { 8 | public string TableName { get; } 9 | 10 | public MemoryTableAttribute(string tableName) 11 | { 12 | this.TableName = tableName; 13 | } 14 | } 15 | 16 | [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] 17 | public class PrimaryKeyAttribute : Attribute 18 | { 19 | public int KeyOrder { get; } 20 | 21 | public PrimaryKeyAttribute(int keyOrder = 0) 22 | { 23 | this.KeyOrder = keyOrder; 24 | } 25 | } 26 | 27 | [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] 28 | public class SecondaryKeyAttribute : Attribute 29 | { 30 | public int IndexNo { get; } 31 | public int KeyOrder { get; } 32 | 33 | public SecondaryKeyAttribute(int indexNo, int keyOrder = 0) 34 | { 35 | this.IndexNo = indexNo; 36 | this.KeyOrder = keyOrder; 37 | } 38 | } 39 | 40 | [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] 41 | public class NonUniqueAttribute : Attribute 42 | { 43 | 44 | } 45 | 46 | [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] 47 | public class StringComparisonOptionAttribute : Attribute 48 | { 49 | public StringComparison StringComparison { get; } 50 | 51 | public StringComparisonOptionAttribute(StringComparison stringComparison) 52 | { 53 | this.StringComparison = stringComparison; 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/MasterMemory.Annotations/MasterMemory.Annotations.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 13 6 | Library 7 | enable 8 | False 9 | Cysharp 10 | true 11 | 1701;1702;1705;1591 12 | 13 | 14 | MasterMemory.Annotations 15 | $(Version) 16 | Cysharp 17 | Cysharp 18 | Attributes of MasterMemory. 19 | https://github.com/Cysharp/MasterMemory 20 | $(PackageProjectUrl) 21 | git 22 | database, embedded, inmemory, unity 23 | MasterMemory 24 | true 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/MasterMemory.SourceGenerator/DiagnosticDescriptors.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace MasterMemory; 7 | 8 | internal sealed class DiagnosticReporter : IEquatable 9 | { 10 | List? diagnostics; 11 | 12 | public bool HasDiagnostics => diagnostics != null && diagnostics.Count != 0; 13 | 14 | public void ReportDiagnostic(DiagnosticDescriptor diagnosticDescriptor, Location location, params object?[]? messageArgs) 15 | { 16 | var diagnostic = Diagnostic.Create(diagnosticDescriptor, location, messageArgs); 17 | if (diagnostics == null) 18 | { 19 | diagnostics = new(); 20 | } 21 | diagnostics.Add(diagnostic); 22 | } 23 | 24 | public void ReportToContext(SourceProductionContext context) 25 | { 26 | if (diagnostics != null) 27 | { 28 | foreach (var item in diagnostics) 29 | { 30 | context.ReportDiagnostic(item); 31 | } 32 | } 33 | } 34 | 35 | public bool Equals(DiagnosticReporter other) 36 | { 37 | // if error, always false and otherwise ignore 38 | if (diagnostics == null && other.diagnostics == null) 39 | { 40 | return true; 41 | } 42 | 43 | return false; 44 | } 45 | } 46 | 47 | internal static class DiagnosticDescriptors 48 | { 49 | const string Category = "GenerateMasterMemory"; 50 | 51 | public static void ReportDiagnostic(this SourceProductionContext context, DiagnosticDescriptor diagnosticDescriptor, Location location, params object?[]? messageArgs) 52 | { 53 | var diagnostic = Diagnostic.Create(diagnosticDescriptor, location, messageArgs); 54 | context.ReportDiagnostic(diagnostic); 55 | } 56 | 57 | public static DiagnosticDescriptor Create(int id, string message) 58 | { 59 | return Create(id, message, message); 60 | } 61 | 62 | public static DiagnosticDescriptor Create(int id, string title, string messageFormat) 63 | { 64 | return new DiagnosticDescriptor( 65 | id: "MAM" + id.ToString("000"), 66 | title: title, 67 | messageFormat: messageFormat, 68 | category: Category, 69 | defaultSeverity: DiagnosticSeverity.Error, 70 | isEnabledByDefault: true); 71 | } 72 | 73 | public static DiagnosticDescriptor RequirePrimaryKey { get; } = Create( 74 | 1, 75 | "MemoryTable does not found PrimaryKey property, Type:{0}."); 76 | 77 | public static DiagnosticDescriptor DuplicatePrimaryKey { get; } = Create( 78 | 2, 79 | "Duplicate PrimaryKey:{0}.{1}"); 80 | 81 | public static DiagnosticDescriptor DuplicateSecondaryKey { get; } = Create( 82 | 3, 83 | "Duplicate SecondaryKey, doesn't allow to add multiple attribute in same attribute list:{0}.{1}"); 84 | } 85 | -------------------------------------------------------------------------------- /src/MasterMemory.SourceGenerator/GeneratorCore/DatabaseBuilderTemplate.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="false" linePragmas="false" language="C#" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ import namespace="System.Linq" #> 4 | <#@ import namespace="System.Text" #> 5 | <#@ import namespace="System.Collections.Generic" #> 6 | // 7 | #pragma warning disable 8 | #nullable enable 9 | 10 | <#= Using #> 11 | 12 | namespace <#= Namespace #> 13 | { 14 | public sealed class <#= ClassName #> : DatabaseBuilderBase 15 | { 16 | public <#= ClassName #>() : this(null) { } 17 | public <#= ClassName #>(MessagePack.IFormatterResolver? resolver) : base(resolver) { } 18 | 19 | <# foreach(var item in GenerationContexts) { #> 20 | public <#= ClassName #> Append(System.Collections.Generic.IEnumerable<<#= item.ClassName #>> dataSource) 21 | { 22 | AppendCore(dataSource, x => <#= item.PrimaryKey.BuildKeyAccessor("x") #>, <#= item.PrimaryKey.BuildComparer() #>); 23 | return this; 24 | } 25 | 26 | <# } #> 27 | } 28 | } -------------------------------------------------------------------------------- /src/MasterMemory.SourceGenerator/GeneratorCore/ImmutableBuilderTemplate.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="false" linePragmas="false" language="C#" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ import namespace="System.Linq" #> 4 | <#@ import namespace="System.Text" #> 5 | <#@ import namespace="System.Collections.Generic" #> 6 | // 7 | #pragma warning disable 8 | #nullable enable 9 | 10 | <#= Using #> 11 | 12 | namespace <#= Namespace #> 13 | { 14 | public sealed class <#= ClassName #> : ImmutableBuilderBase 15 | { 16 | <#= PrefixClassName #>MemoryDatabase memory; 17 | 18 | public <#= ClassName #>(<#= PrefixClassName #>MemoryDatabase memory) 19 | { 20 | this.memory = memory; 21 | } 22 | 23 | public <#= PrefixClassName #>MemoryDatabase Build() 24 | { 25 | return memory; 26 | } 27 | 28 | <# for(var i = 0; i < GenerationContexts.Length; i++) { var item = GenerationContexts[i]; #> 29 | public void ReplaceAll(System.Collections.Generic.IList<<#= item.ClassName #>> data) 30 | { 31 | var newData = CloneAndSortBy(data, x => <#= item.PrimaryKey.BuildKeyAccessor("x") #>, <#= item.PrimaryKey.BuildComparer() #>); 32 | var table = new <#= item.ClassName #>Table(newData); 33 | memory = new <#= PrefixClassName #>MemoryDatabase( 34 | <# for(var j = 0; j < GenerationContexts.Length; j++) { var item2 = GenerationContexts[j]; #> 35 | <#= (i == j) ? "table" : "memory." + item2.ClassName + "Table" #><#= (j == GenerationContexts.Length - 1) ? "" : "," #> 36 | <# } #> 37 | ); 38 | } 39 | 40 | <# if(!item.PrimaryKey.IsNonUnique) { #> 41 | public void Remove<#= item.ClassName #>(<#= item.PrimaryKey.BuildTypeName() #>[] keys) 42 | { 43 | var data = RemoveCore(memory.<#= item.ClassName #>Table.GetRawDataUnsafe(), keys, x => <#= item.PrimaryKey.BuildKeyAccessor("x") #>, <#= item.PrimaryKey.BuildComparer() #>); 44 | var newData = CloneAndSortBy(data, x => <#= item.PrimaryKey.BuildKeyAccessor("x") #>, <#= item.PrimaryKey.BuildComparer() #>); 45 | var table = new <#= item.ClassName #>Table(newData); 46 | memory = new <#= PrefixClassName #>MemoryDatabase( 47 | <# for(var j = 0; j < GenerationContexts.Length; j++) { var item2 = GenerationContexts[j]; #> 48 | <#= (i == j) ? "table" : "memory." + item2.ClassName + "Table" #><#= (j == GenerationContexts.Length - 1) ? "" : "," #> 49 | <# } #> 50 | ); 51 | } 52 | 53 | public void Diff(<#= item.ClassName #>[] addOrReplaceData) 54 | { 55 | var data = DiffCore(memory.<#= item.ClassName #>Table.GetRawDataUnsafe(), addOrReplaceData, x => <#= item.PrimaryKey.BuildKeyAccessor("x") #>, <#= item.PrimaryKey.BuildComparer() #>); 56 | var newData = CloneAndSortBy(data, x => <#= item.PrimaryKey.BuildKeyAccessor("x") #>, <#= item.PrimaryKey.BuildComparer() #>); 57 | var table = new <#= item.ClassName #>Table(newData); 58 | memory = new <#= PrefixClassName #>MemoryDatabase( 59 | <# for(var j = 0; j < GenerationContexts.Length; j++) { var item2 = GenerationContexts[j]; #> 60 | <#= (i == j) ? "table" : "memory." + item2.ClassName + "Table" #><#= (j == GenerationContexts.Length - 1) ? "" : "," #> 61 | <# } #> 62 | ); 63 | } 64 | <# } #> 65 | 66 | <# } #> 67 | } 68 | } -------------------------------------------------------------------------------- /src/MasterMemory.SourceGenerator/GeneratorCore/MessagePackResolverTemplate.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="false" linePragmas="false" language="C#" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ import namespace="System.Linq" #> 4 | <#@ import namespace="System.Text" #> 5 | <#@ import namespace="System.Collections.Generic" #> 6 | // 7 | #pragma warning disable 8 | #nullable enable 9 | 10 | <#= Using #> 11 | 12 | namespace <#= Namespace #> 13 | { 14 | public class <#= ClassName #> : global::MessagePack.IFormatterResolver 15 | { 16 | public static readonly global::MessagePack.IFormatterResolver Instance = new <#= ClassName #>(); 17 | 18 | <#= ClassName #>() 19 | { 20 | 21 | } 22 | 23 | public global::MessagePack.Formatters.IMessagePackFormatter? GetFormatter() 24 | { 25 | return FormatterCache.formatter; 26 | } 27 | 28 | static class FormatterCache 29 | { 30 | public static readonly global::MessagePack.Formatters.IMessagePackFormatter? formatter; 31 | 32 | static FormatterCache() 33 | { 34 | var f = <#= ClassName #>GetFormatterHelper.GetFormatter(typeof(T)); 35 | if (f != null) 36 | { 37 | formatter = (global::MessagePack.Formatters.IMessagePackFormatter)f; 38 | } 39 | } 40 | } 41 | } 42 | 43 | internal static class <#= ClassName #>GetFormatterHelper 44 | { 45 | static readonly global::System.Collections.Generic.Dictionary lookup; 46 | 47 | static <#= ClassName #>GetFormatterHelper() 48 | { 49 | lookup = new global::System.Collections.Generic.Dictionary(<#= GenerationContexts.Length #>) 50 | { 51 | <# for(var i = 0; i < GenerationContexts.Length; i++) { var item = GenerationContexts[i]; #> 52 | {typeof(<#= item.ClassName #>[]), <#= i #> }, 53 | <# } #> 54 | }; 55 | } 56 | 57 | internal static object? GetFormatter(Type t) 58 | { 59 | int key; 60 | if (!lookup.TryGetValue(t, out key)) return null; 61 | 62 | switch (key) 63 | { 64 | <# for(var i = 0; i < GenerationContexts.Length; i++) { var item = GenerationContexts[i]; #> 65 | case <#= i #>: return new MessagePack.Formatters.ArrayFormatter<<#= item.ClassName #>>(); 66 | <# } #> 67 | default: return null; 68 | } 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /src/MasterMemory.SourceGenerator/GeneratorCore/Template.cs: -------------------------------------------------------------------------------- 1 | #nullable disable 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace MasterMemory.GeneratorCore 8 | { 9 | public partial class DatabaseBuilderTemplate 10 | { 11 | public string Namespace { get; set; } 12 | public string Using { get; set; } 13 | public string PrefixClassName { get; set; } 14 | public GenerationContext[] GenerationContexts { get; set; } 15 | 16 | public string ClassName => PrefixClassName + "DatabaseBuilder"; 17 | } 18 | 19 | public partial class MemoryDatabaseTemplate 20 | { 21 | public string Namespace { get; set; } 22 | public string Using { get; set; } 23 | public string PrefixClassName { get; set; } 24 | public GenerationContext[] GenerationContexts { get; set; } 25 | public string ClassName => PrefixClassName + "MemoryDatabase"; 26 | } 27 | 28 | public partial class MetaMemoryDatabaseTemplate 29 | { 30 | public string Namespace { get; set; } 31 | public string Using { get; set; } 32 | public string PrefixClassName { get; set; } 33 | public GenerationContext[] GenerationContexts { get; set; } 34 | public string ClassName => PrefixClassName + "MetaMemoryDatabase"; 35 | } 36 | 37 | public partial class ImmutableBuilderTemplate 38 | { 39 | public string Namespace { get; set; } 40 | public string Using { get; set; } 41 | public string PrefixClassName { get; set; } 42 | public GenerationContext[] GenerationContexts { get; set; } 43 | public string ClassName => PrefixClassName + "ImmutableBuilder"; 44 | } 45 | 46 | public partial class MessagePackResolverTemplate 47 | { 48 | public string Namespace { get; set; } 49 | public string Using { get; set; } 50 | public string PrefixClassName { get; set; } 51 | public GenerationContext[] GenerationContexts { get; set; } 52 | public string ClassName => PrefixClassName + "MasterMemoryResolver"; 53 | } 54 | 55 | public partial class TableTemplate 56 | { 57 | public string Namespace { get; set; } 58 | public string Using { get; set; } 59 | public string PrefixClassName { get; set; } 60 | public GenerationContext GenerationContext { get; set; } 61 | 62 | public bool ThrowKeyIfNotFound { get; set; } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/MasterMemory.SourceGenerator/MasterMemory.GeneratorCore.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | latest 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | DatabaseBuilderTemplate.tt 19 | True 20 | True 21 | 22 | 23 | True 24 | True 25 | ImmutableBuilderTemplate.tt 26 | 27 | 28 | MemoryDatabaseTemplate.tt 29 | True 30 | True 31 | 32 | 33 | True 34 | True 35 | TableTemplate.tt 36 | 37 | 38 | True 39 | True 40 | MessagePackResolverTemplate.tt 41 | 42 | 43 | 44 | 45 | 46 | DatabaseBuilderTemplate.cs 47 | TextTemplatingFilePreprocessor 48 | 49 | 50 | ImmutableBuilderTemplate.cs 51 | TextTemplatingFilePreprocessor 52 | 53 | 54 | MemoryDatabaseTemplate.cs 55 | TextTemplatingFilePreprocessor 56 | 57 | 58 | TableTemplate.cs 59 | TextTemplatingFilePreprocessor 60 | 61 | 62 | MessagePackResolverTemplate.cs 63 | TextTemplatingFilePreprocessor 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/MasterMemory.SourceGenerator/MasterMemory.SourceGenerator.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 13 6 | enable 7 | enable 8 | true 9 | cs 10 | MasterMemory 11 | 12 | 13 | false 14 | 15 | 16 | 17 | 18 | 19 | 20 | all 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | 23 | 24 | 25 | 26 | 27 | TextTemplatingFilePreprocessor 28 | DatabaseBuilderTemplate.cs 29 | 30 | 31 | TextTemplatingFilePreprocessor 32 | ImmutableBuilderTemplate.cs 33 | 34 | 35 | TextTemplatingFilePreprocessor 36 | MemoryDatabaseTemplate.cs 37 | 38 | 39 | TextTemplatingFilePreprocessor 40 | MessagePackResolverTemplate.cs 41 | 42 | 43 | TextTemplatingFilePreprocessor 44 | TableTemplate.cs 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | True 55 | True 56 | DatabaseBuilderTemplate.tt 57 | 58 | 59 | True 60 | True 61 | ImmutableBuilderTemplate.tt 62 | 63 | 64 | True 65 | True 66 | MemoryDatabaseTemplate.tt 67 | 68 | 69 | True 70 | True 71 | MessagePackResolverTemplate.tt 72 | 73 | 74 | True 75 | True 76 | TableTemplate.tt 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /src/MasterMemory.SourceGenerator/MasterMemoryGeneratorOptions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | 3 | 4 | namespace MasterMemory.SourceGenerator; 5 | 6 | readonly record struct MasterMemoryGeneratorOptions(string? Namespace, string PrefixClassName, bool IsReturnNullIfKeyNotFound) 7 | { 8 | public static MasterMemoryGeneratorOptions FromAttribute(AttributeData attributeData) 9 | { 10 | var args = attributeData.NamedArguments; 11 | 12 | var ns = args.FirstOrDefault(x => x.Key == nameof(Namespace)).Value.Value as string ?? null; 13 | var prefix = args.FirstOrDefault(x => x.Key == nameof(PrefixClassName)).Value.Value as string ?? ""; 14 | var isReturnNull = args.FirstOrDefault(x => x.Key == nameof(IsReturnNullIfKeyNotFound)).Value.Value as bool? ?? null; 15 | 16 | return new MasterMemoryGeneratorOptions(ns, prefix, isReturnNull ?? false); 17 | } 18 | 19 | public static void EmitAttribute(IncrementalGeneratorPostInitializationContext context) 20 | { 21 | context.AddSource("MasterMemory.MasterMemoryGeneratorOptions.g.cs", """ 22 | // 23 | #pragma warning disable 24 | #nullable enable 25 | 26 | using System; 27 | 28 | namespace MasterMemory 29 | { 30 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] 31 | internal sealed class MasterMemoryGeneratorOptionsAttribute : Attribute 32 | { 33 | public string? Namespace { get; set; } = null; 34 | public string PrefixClassName { get; set; } = ""; 35 | public bool IsReturnNullIfKeyNotFound { get; set; } = false; 36 | } 37 | } 38 | """); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/MasterMemory.SourceGenerator/Polyfill/System.CodeDom.cs: -------------------------------------------------------------------------------- 1 | #nullable disable 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace System.CodeDom.Compiler 8 | { 9 | public class CompilerError 10 | { 11 | public string ErrorText { get; set; } 12 | public bool IsWarning { get; set; } 13 | } 14 | 15 | public class CompilerErrorCollection 16 | { 17 | public void Add(CompilerError error) 18 | { 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/MasterMemory.SourceGenerator/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Profile 1": { 4 | "commandName": "DebugRoslynComponent", 5 | "targetProject": "..\\..\\sandbox\\GeneratorSandbox\\GeneratorSandbox.csproj" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /src/MasterMemory.SourceGenerator/Utility/EquatableArray.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace MasterMemory; 5 | 6 | public readonly struct EquatableArray : IEquatable>, IEnumerable 7 | where T : IEquatable 8 | { 9 | readonly T[]? array; 10 | 11 | public EquatableArray() // for collection literal [] 12 | { 13 | array = []; 14 | } 15 | 16 | public EquatableArray(T[] array) 17 | { 18 | this.array = array; 19 | } 20 | 21 | public static implicit operator EquatableArray(T[] array) 22 | { 23 | return new EquatableArray(array); 24 | } 25 | 26 | public ref readonly T this[int index] 27 | { 28 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 29 | get => ref array![index]; 30 | } 31 | 32 | public int Length => array!.Length; 33 | 34 | public ReadOnlySpan AsSpan() 35 | { 36 | return array.AsSpan(); 37 | } 38 | 39 | public ReadOnlySpan.Enumerator GetEnumerator() 40 | { 41 | return AsSpan().GetEnumerator(); 42 | } 43 | 44 | IEnumerator IEnumerable.GetEnumerator() 45 | { 46 | return array.AsEnumerable().GetEnumerator(); 47 | } 48 | 49 | IEnumerator IEnumerable.GetEnumerator() 50 | { 51 | return array.AsEnumerable().GetEnumerator(); 52 | } 53 | 54 | public bool Equals(EquatableArray other) 55 | { 56 | return AsSpan().SequenceEqual(other.AsSpan()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/MasterMemory.SourceGenerator/Utility/IgnoreEquality.cs: -------------------------------------------------------------------------------- 1 | namespace MasterMemory; 2 | 3 | public readonly struct IgnoreEquality(T value) : IEquatable> 4 | { 5 | public readonly T Value => value; 6 | 7 | public static implicit operator IgnoreEquality(T value) 8 | { 9 | return new IgnoreEquality(value); 10 | } 11 | 12 | public static implicit operator T(IgnoreEquality value) 13 | { 14 | return value.Value; 15 | } 16 | 17 | public bool Equals(IgnoreEquality other) 18 | { 19 | // always true to ignore equality check. 20 | return true; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/Assets/NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/Assets/NuGet.config.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e5322b2ac44bca4478137f3076edc3bb 3 | labels: 4 | - NuGetForUnity 5 | PluginImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | iconMap: {} 9 | executionOrder: {} 10 | defineConstraints: [] 11 | isPreloaded: 0 12 | isOverridable: 0 13 | isExplicitlyReferenced: 0 14 | validateReferences: 1 15 | platformData: 16 | - first: 17 | Any: 18 | second: 19 | enabled: 0 20 | settings: {} 21 | - first: 22 | Editor: Editor 23 | second: 24 | enabled: 0 25 | settings: 26 | DefaultValueInitialized: true 27 | - first: 28 | Windows Store Apps: WindowsStoreApps 29 | second: 30 | enabled: 1 31 | settings: {} 32 | userData: 33 | assetBundleName: 34 | assetBundleVariant: 35 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/Assets/Packages.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 26358cf27391727439065c2117eb2e52 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/Assets/Scenes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3e4672a57ce755a44805bc58b4ddea29 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/Assets/Scenes/Main.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d5ed035d1a1185e43a6e9d45e3d68f1f 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/Assets/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dfc2745c192a6764a8f038393ed2455c 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/Assets/Scripts/NewBehaviourScript.cs: -------------------------------------------------------------------------------- 1 | using MasterMemory; 2 | using MessagePack; 3 | using MessagePack.Resolvers; 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using UnityEngine; 8 | using MyProj; 9 | 10 | [assembly: MasterMemoryGeneratorOptions(Namespace = "MyProj")] 11 | 12 | // If you want to use init, copy-and-paste this. 13 | namespace System.Runtime.CompilerServices 14 | { 15 | internal sealed class IsExternalInit { } 16 | } 17 | 18 | 19 | 20 | public class NewBehaviourScript : MonoBehaviour 21 | { 22 | // Start is called before the first frame update 23 | void Start() 24 | { 25 | 26 | } 27 | 28 | // Update is called once per frame 29 | void Update() 30 | { 31 | 32 | } 33 | } 34 | 35 | public enum Gender 36 | { 37 | Male, Female, Unknown 38 | } 39 | 40 | // table definition marked by MemoryTableAttribute. 41 | // database-table must be serializable by MessagePack-CSsharp 42 | [MemoryTable("person"), MessagePackObject(true)] 43 | public record Person 44 | { 45 | // index definition by attributes. 46 | [PrimaryKey] 47 | public int PersonId { get; init; } 48 | 49 | // secondary index can add multiple(discriminated by index-number). 50 | [SecondaryKey(0), NonUnique] 51 | [SecondaryKey(1, keyOrder: 1), NonUnique] 52 | public int Age { get; init; } 53 | 54 | [SecondaryKey(2), NonUnique] 55 | [SecondaryKey(1, keyOrder: 0), NonUnique] 56 | public Gender Gender { get; init; } 57 | 58 | public string Name { get; init; } 59 | } 60 | 61 | public static class Initializer 62 | { 63 | [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] 64 | public static void SetupMessagePackResolver() 65 | { 66 | // Create CompositeResolver 67 | StaticCompositeResolver.Instance.Register(new[]{ 68 | MasterMemoryResolver.Instance, // set MasterMemory generated resolver 69 | StandardResolver.Instance // set default MessagePack resolver 70 | }); 71 | 72 | // Set as default 73 | var options = MessagePackSerializerOptions.Standard.WithResolver(StaticCompositeResolver.Instance); 74 | MessagePackSerializer.DefaultOptions = options; 75 | } 76 | } -------------------------------------------------------------------------------- /src/MasterMemory.Unity/Assets/Scripts/NewBehaviourScript.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a7e2d905f1c43ee42914ee6de131c41e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/Assets/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/Assets/packages.config.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 44858e3667fc6e44c8fc19fd02574910 3 | labels: 4 | - NuGetForUnity 5 | PluginImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | iconMap: {} 9 | executionOrder: {} 10 | defineConstraints: [] 11 | isPreloaded: 0 12 | isOverridable: 0 13 | isExplicitlyReferenced: 0 14 | validateReferences: 1 15 | platformData: 16 | - first: 17 | Any: 18 | second: 19 | enabled: 1 20 | settings: {} 21 | userData: 22 | assetBundleName: 23 | assetBundleVariant: 24 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/Packages/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "com.cysharp.runtimeunittesttoolkit": "https://github.com/Cysharp/RuntimeUnitTestToolkit.git?path=RuntimeUnitTestToolkit/Assets/RuntimeUnitTestToolkit#2.6.0", 4 | "com.github-glitchenzo.nugetforunity": "https://github.com/GlitchEnzo/NuGetForUnity.git?path=/src/NuGetForUnity", 5 | "com.github.mastermemory.internal": "file:/../../src/MasterMemory/bin/Debug/netstandard2.0", 6 | "com.unity.ide.rider": "3.0.31", 7 | "com.unity.ide.visualstudio": "2.0.22", 8 | "com.unity.ide.vscode": "1.2.5", 9 | "com.unity.test-framework": "1.1.33", 10 | "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.9", 11 | "com.unity.ugui": "1.0.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/Packages/packages-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "com.cysharp.runtimeunittesttoolkit": { 4 | "version": "https://github.com/Cysharp/RuntimeUnitTestToolkit.git?path=RuntimeUnitTestToolkit/Assets/RuntimeUnitTestToolkit#2.6.0", 5 | "depth": 0, 6 | "source": "git", 7 | "dependencies": {}, 8 | "hash": "4e3dbfaa9c40b5cfdcb71a1d4e8bca0d45ca1055" 9 | }, 10 | "com.github-glitchenzo.nugetforunity": { 11 | "version": "https://github.com/GlitchEnzo/NuGetForUnity.git?path=/src/NuGetForUnity", 12 | "depth": 0, 13 | "source": "git", 14 | "dependencies": {}, 15 | "hash": "7c80e98d3b56ecbcf854a9336458b6b0dedf1b8f" 16 | }, 17 | "com.github.mastermemory.internal": { 18 | "version": "file:C:/Users/S04451/Documents/GitHub/MasterMemory/src/MasterMemory/bin/Debug/netstandard2.0", 19 | "depth": 0, 20 | "source": "local", 21 | "dependencies": {} 22 | }, 23 | "com.unity.ext.nunit": { 24 | "version": "1.0.6", 25 | "depth": 1, 26 | "source": "registry", 27 | "dependencies": {}, 28 | "url": "https://packages.unity.com" 29 | }, 30 | "com.unity.ide.rider": { 31 | "version": "3.0.31", 32 | "depth": 0, 33 | "source": "registry", 34 | "dependencies": { 35 | "com.unity.ext.nunit": "1.0.6" 36 | }, 37 | "url": "https://packages.unity.com" 38 | }, 39 | "com.unity.ide.visualstudio": { 40 | "version": "2.0.22", 41 | "depth": 0, 42 | "source": "registry", 43 | "dependencies": { 44 | "com.unity.test-framework": "1.1.9" 45 | }, 46 | "url": "https://packages.unity.com" 47 | }, 48 | "com.unity.ide.vscode": { 49 | "version": "1.2.5", 50 | "depth": 0, 51 | "source": "registry", 52 | "dependencies": {}, 53 | "url": "https://packages.unity.com" 54 | }, 55 | "com.unity.sysroot": { 56 | "version": "2.0.10", 57 | "depth": 1, 58 | "source": "registry", 59 | "dependencies": {}, 60 | "url": "https://packages.unity.com" 61 | }, 62 | "com.unity.sysroot.linux-x86_64": { 63 | "version": "2.0.9", 64 | "depth": 1, 65 | "source": "registry", 66 | "dependencies": { 67 | "com.unity.sysroot": "2.0.10" 68 | }, 69 | "url": "https://packages.unity.com" 70 | }, 71 | "com.unity.test-framework": { 72 | "version": "1.1.33", 73 | "depth": 0, 74 | "source": "registry", 75 | "dependencies": { 76 | "com.unity.ext.nunit": "1.0.6", 77 | "com.unity.modules.imgui": "1.0.0", 78 | "com.unity.modules.jsonserialize": "1.0.0" 79 | }, 80 | "url": "https://packages.unity.com" 81 | }, 82 | "com.unity.toolchain.win-x86_64-linux-x86_64": { 83 | "version": "2.0.9", 84 | "depth": 0, 85 | "source": "registry", 86 | "dependencies": { 87 | "com.unity.sysroot": "2.0.10", 88 | "com.unity.sysroot.linux-x86_64": "2.0.9" 89 | }, 90 | "url": "https://packages.unity.com" 91 | }, 92 | "com.unity.ugui": { 93 | "version": "1.0.0", 94 | "depth": 0, 95 | "source": "builtin", 96 | "dependencies": { 97 | "com.unity.modules.ui": "1.0.0", 98 | "com.unity.modules.imgui": "1.0.0" 99 | } 100 | }, 101 | "com.unity.modules.imgui": { 102 | "version": "1.0.0", 103 | "depth": 1, 104 | "source": "builtin", 105 | "dependencies": {} 106 | }, 107 | "com.unity.modules.jsonserialize": { 108 | "version": "1.0.0", 109 | "depth": 1, 110 | "source": "builtin", 111 | "dependencies": {} 112 | }, 113 | "com.unity.modules.ui": { 114 | "version": "1.0.0", 115 | "depth": 1, 116 | "source": "builtin", 117 | "dependencies": {} 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!11 &1 4 | AudioManager: 5 | m_ObjectHideFlags: 0 6 | m_Volume: 1 7 | Rolloff Scale: 1 8 | Doppler Factor: 1 9 | Default Speaker Mode: 2 10 | m_SampleRate: 0 11 | m_DSPBufferSize: 1024 12 | m_VirtualVoiceCount: 512 13 | m_RealVoiceCount: 32 14 | m_SpatializerPlugin: 15 | m_AmbisonicDecoderPlugin: 16 | m_DisableAudio: 0 17 | m_VirtualizeEffects: 1 18 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/ClusterInputManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!236 &1 4 | ClusterInputManager: 5 | m_ObjectHideFlags: 0 6 | m_Inputs: [] 7 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!55 &1 4 | PhysicsManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 7 7 | m_Gravity: {x: 0, y: -9.81, z: 0} 8 | m_DefaultMaterial: {fileID: 0} 9 | m_BounceThreshold: 2 10 | m_SleepThreshold: 0.005 11 | m_DefaultContactOffset: 0.01 12 | m_DefaultSolverIterations: 6 13 | m_DefaultSolverVelocityIterations: 1 14 | m_QueriesHitBackfaces: 0 15 | m_QueriesHitTriggers: 1 16 | m_EnableAdaptiveForce: 0 17 | m_ClothInterCollisionDistance: 0 18 | m_ClothInterCollisionStiffness: 0 19 | m_ContactsGeneration: 1 20 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 21 | m_AutoSimulation: 1 22 | m_AutoSyncTransforms: 0 23 | m_ReuseCollisionCallbacks: 1 24 | m_ClothInterCollisionSettingsToggle: 0 25 | m_ContactPairsMode: 0 26 | m_BroadphaseType: 0 27 | m_WorldBounds: 28 | m_Center: {x: 0, y: 0, z: 0} 29 | m_Extent: {x: 250, y: 250, z: 250} 30 | m_WorldSubdivisions: 8 31 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1045 &1 4 | EditorBuildSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_Scenes: 8 | - enabled: 1 9 | path: Assets/Scenes/SampleScene.unity 10 | guid: 2cda990e2423bbf4892e6590ba056729 11 | m_configObjects: {} 12 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!159 &1 4 | EditorSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 7 7 | m_ExternalVersionControlSupport: Visible Meta Files 8 | m_SerializationMode: 2 9 | m_LineEndingsForNewScripts: 2 10 | m_DefaultBehaviorMode: 1 11 | m_SpritePackerMode: 4 12 | m_SpritePackerPaddingPower: 1 13 | m_EtcTextureCompressorBehavior: 1 14 | m_EtcTextureFastCompressor: 1 15 | m_EtcTextureNormalCompressor: 2 16 | m_EtcTextureBestCompressor: 4 17 | m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd 18 | m_ProjectGenerationRootNamespace: 19 | m_UserGeneratedProjectSuffix: 20 | m_CollabEditorSettings: 21 | inProgressEnabled: 1 22 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!30 &1 4 | GraphicsSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 12 7 | m_Deferred: 8 | m_Mode: 1 9 | m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} 10 | m_DeferredReflections: 11 | m_Mode: 1 12 | m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} 13 | m_ScreenSpaceShadows: 14 | m_Mode: 1 15 | m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} 16 | m_LegacyDeferred: 17 | m_Mode: 1 18 | m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} 19 | m_DepthNormals: 20 | m_Mode: 1 21 | m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} 22 | m_MotionVectors: 23 | m_Mode: 1 24 | m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} 25 | m_LightHalo: 26 | m_Mode: 1 27 | m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} 28 | m_LensFlare: 29 | m_Mode: 1 30 | m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} 31 | m_AlwaysIncludedShaders: 32 | - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} 33 | - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} 34 | - {fileID: 16000, guid: 0000000000000000f000000000000000, type: 0} 35 | - {fileID: 16001, guid: 0000000000000000f000000000000000, type: 0} 36 | - {fileID: 17000, guid: 0000000000000000f000000000000000, type: 0} 37 | m_PreloadedShaders: [] 38 | m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, 39 | type: 0} 40 | m_CustomRenderPipeline: {fileID: 0} 41 | m_TransparencySortMode: 0 42 | m_TransparencySortAxis: {x: 0, y: 0, z: 1} 43 | m_DefaultRenderingPath: 1 44 | m_DefaultMobileRenderingPath: 1 45 | m_TierSettings: [] 46 | m_LightmapStripping: 0 47 | m_FogStripping: 0 48 | m_InstancingStripping: 0 49 | m_LightmapKeepPlain: 1 50 | m_LightmapKeepDirCombined: 1 51 | m_LightmapKeepDynamicPlain: 1 52 | m_LightmapKeepDynamicDirCombined: 1 53 | m_LightmapKeepShadowMask: 1 54 | m_LightmapKeepSubtractive: 1 55 | m_FogKeepLinear: 1 56 | m_FogKeepExp: 1 57 | m_FogKeepExp2: 1 58 | m_AlbedoSwatchInfos: [] 59 | m_LightsUseLinearIntensity: 0 60 | m_LightsUseColorTemperature: 0 61 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/MemorySettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!387306366 &1 4 | MemorySettings: 5 | m_ObjectHideFlags: 0 6 | m_EditorMemorySettings: 7 | m_MainAllocatorBlockSize: -1 8 | m_ThreadAllocatorBlockSize: -1 9 | m_MainGfxBlockSize: -1 10 | m_ThreadGfxBlockSize: -1 11 | m_CacheBlockSize: -1 12 | m_TypetreeBlockSize: -1 13 | m_ProfilerBlockSize: -1 14 | m_ProfilerEditorBlockSize: -1 15 | m_BucketAllocatorGranularity: -1 16 | m_BucketAllocatorBucketsCount: -1 17 | m_BucketAllocatorBlockSize: -1 18 | m_BucketAllocatorBlockCount: -1 19 | m_ProfilerBucketAllocatorGranularity: -1 20 | m_ProfilerBucketAllocatorBucketsCount: -1 21 | m_ProfilerBucketAllocatorBlockSize: -1 22 | m_ProfilerBucketAllocatorBlockCount: -1 23 | m_TempAllocatorSizeMain: -1 24 | m_JobTempAllocatorBlockSize: -1 25 | m_BackgroundJobTempAllocatorBlockSize: -1 26 | m_JobTempAllocatorReducedBlockSize: -1 27 | m_TempAllocatorSizeGIBakingWorker: -1 28 | m_TempAllocatorSizeNavMeshWorker: -1 29 | m_TempAllocatorSizeAudioWorker: -1 30 | m_TempAllocatorSizeCloudWorker: -1 31 | m_TempAllocatorSizeGfx: -1 32 | m_TempAllocatorSizeJobWorker: -1 33 | m_TempAllocatorSizeBackgroundWorker: -1 34 | m_TempAllocatorSizePreloadManager: -1 35 | m_PlatformMemorySettings: {} 36 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!126 &1 4 | NavMeshProjectSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | areas: 8 | - name: Walkable 9 | cost: 1 10 | - name: Not Walkable 11 | cost: 1 12 | - name: Jump 13 | cost: 2 14 | - name: 15 | cost: 1 16 | - name: 17 | cost: 1 18 | - name: 19 | cost: 1 20 | - name: 21 | cost: 1 22 | - name: 23 | cost: 1 24 | - name: 25 | cost: 1 26 | - name: 27 | cost: 1 28 | - name: 29 | cost: 1 30 | - name: 31 | cost: 1 32 | - name: 33 | cost: 1 34 | - name: 35 | cost: 1 36 | - name: 37 | cost: 1 38 | - name: 39 | cost: 1 40 | - name: 41 | cost: 1 42 | - name: 43 | cost: 1 44 | - name: 45 | cost: 1 46 | - name: 47 | cost: 1 48 | - name: 49 | cost: 1 50 | - name: 51 | cost: 1 52 | - name: 53 | cost: 1 54 | - name: 55 | cost: 1 56 | - name: 57 | cost: 1 58 | - name: 59 | cost: 1 60 | - name: 61 | cost: 1 62 | - name: 63 | cost: 1 64 | - name: 65 | cost: 1 66 | - name: 67 | cost: 1 68 | - name: 69 | cost: 1 70 | - name: 71 | cost: 1 72 | m_LastAgentTypeID: -887442657 73 | m_Settings: 74 | - serializedVersion: 2 75 | agentTypeID: 0 76 | agentRadius: 0.5 77 | agentHeight: 2 78 | agentSlope: 45 79 | agentClimb: 0.75 80 | ledgeDropHeight: 0 81 | maxJumpAcrossDistance: 0 82 | minRegionArea: 2 83 | manualCellSize: 0 84 | cellSize: 0.16666667 85 | manualTileSize: 0 86 | tileSize: 256 87 | accuratePlacement: 0 88 | debug: 89 | m_Flags: 0 90 | m_SettingNames: 91 | - Humanoid 92 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/NetworkManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!149 &1 4 | NetworkManager: 5 | m_ObjectHideFlags: 0 6 | m_DebugLevel: 0 7 | m_Sendrate: 15 8 | m_AssetToPrefab: {} 9 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/PackageManagerSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!114 &1 4 | MonoBehaviour: 5 | m_ObjectHideFlags: 53 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_GameObject: {fileID: 0} 10 | m_Enabled: 1 11 | m_EditorHideFlags: 0 12 | m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} 13 | m_Name: 14 | m_EditorClassIdentifier: 15 | m_EnablePreReleasePackages: 0 16 | m_AdvancedSettingsExpanded: 1 17 | m_ScopedRegistriesSettingsExpanded: 1 18 | m_SeeAllPackageVersions: 0 19 | m_DismissPreviewPackagesInUse: 0 20 | oneTimeWarningShown: 0 21 | m_Registries: 22 | - m_Id: main 23 | m_Name: 24 | m_Url: https://packages.unity.com 25 | m_Scopes: [] 26 | m_IsDefault: 1 27 | m_Capabilities: 7 28 | m_ConfigSource: 0 29 | m_UserSelectedRegistryName: 30 | m_UserAddingNewScopedRegistry: 0 31 | m_RegistryInfoDraft: 32 | m_Modified: 0 33 | m_ErrorMessage: 34 | m_UserModificationsInstanceId: -856 35 | m_OriginalInstanceId: -858 36 | m_LoadAssets: 0 37 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!19 &1 4 | Physics2DSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 4 7 | m_Gravity: {x: 0, y: -9.81} 8 | m_DefaultMaterial: {fileID: 0} 9 | m_VelocityIterations: 8 10 | m_PositionIterations: 3 11 | m_VelocityThreshold: 1 12 | m_MaxLinearCorrection: 0.2 13 | m_MaxAngularCorrection: 8 14 | m_MaxTranslationSpeed: 100 15 | m_MaxRotationSpeed: 360 16 | m_BaumgarteScale: 0.2 17 | m_BaumgarteTimeOfImpactScale: 0.75 18 | m_TimeToSleep: 0.5 19 | m_LinearSleepTolerance: 0.01 20 | m_AngularSleepTolerance: 2 21 | m_DefaultContactOffset: 0.01 22 | m_JobOptions: 23 | serializedVersion: 2 24 | useMultithreading: 0 25 | useConsistencySorting: 0 26 | m_InterpolationPosesPerJob: 100 27 | m_NewContactsPerJob: 30 28 | m_CollideContactsPerJob: 100 29 | m_ClearFlagsPerJob: 200 30 | m_ClearBodyForcesPerJob: 200 31 | m_SyncDiscreteFixturesPerJob: 50 32 | m_SyncContinuousFixturesPerJob: 50 33 | m_FindNearestContactsPerJob: 100 34 | m_UpdateTriggerContactsPerJob: 100 35 | m_IslandSolverCostThreshold: 100 36 | m_IslandSolverBodyCostScale: 1 37 | m_IslandSolverContactCostScale: 10 38 | m_IslandSolverJointCostScale: 10 39 | m_IslandSolverBodiesPerJob: 50 40 | m_IslandSolverContactsPerJob: 50 41 | m_AutoSimulation: 1 42 | m_QueriesHitTriggers: 1 43 | m_QueriesStartInColliders: 1 44 | m_CallbacksOnDisable: 1 45 | m_ReuseCollisionCallbacks: 0 46 | m_AutoSyncTransforms: 0 47 | m_AlwaysShowColliders: 0 48 | m_ShowColliderSleep: 1 49 | m_ShowColliderContacts: 0 50 | m_ShowColliderAABB: 0 51 | m_ContactArrowScale: 0.2 52 | m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} 53 | m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} 54 | m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} 55 | m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} 56 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 57 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/PresetManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1386491679 &1 4 | PresetManager: 5 | m_ObjectHideFlags: 0 6 | m_DefaultList: [] 7 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 2022.3.12f1 2 | m_EditorVersionWithRevision: 2022.3.12f1 (4fe6e059c7ef) 3 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!47 &1 4 | QualitySettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 5 7 | m_CurrentQuality: 3 8 | m_QualitySettings: 9 | - serializedVersion: 2 10 | name: Very Low 11 | pixelLightCount: 0 12 | shadows: 0 13 | shadowResolution: 0 14 | shadowProjection: 1 15 | shadowCascades: 1 16 | shadowDistance: 15 17 | shadowNearPlaneOffset: 3 18 | shadowCascade2Split: 0.33333334 19 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 20 | shadowmaskMode: 0 21 | blendWeights: 1 22 | textureQuality: 1 23 | anisotropicTextures: 0 24 | antiAliasing: 0 25 | softParticles: 0 26 | softVegetation: 0 27 | realtimeReflectionProbes: 0 28 | billboardsFaceCameraPosition: 0 29 | vSyncCount: 0 30 | lodBias: 0.3 31 | maximumLODLevel: 0 32 | particleRaycastBudget: 4 33 | asyncUploadTimeSlice: 2 34 | asyncUploadBufferSize: 16 35 | resolutionScalingFixedDPIFactor: 1 36 | excludedTargetPlatforms: [] 37 | - serializedVersion: 2 38 | name: Low 39 | pixelLightCount: 0 40 | shadows: 0 41 | shadowResolution: 0 42 | shadowProjection: 1 43 | shadowCascades: 1 44 | shadowDistance: 20 45 | shadowNearPlaneOffset: 3 46 | shadowCascade2Split: 0.33333334 47 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 48 | shadowmaskMode: 0 49 | blendWeights: 2 50 | textureQuality: 0 51 | anisotropicTextures: 0 52 | antiAliasing: 0 53 | softParticles: 0 54 | softVegetation: 0 55 | realtimeReflectionProbes: 0 56 | billboardsFaceCameraPosition: 0 57 | vSyncCount: 0 58 | lodBias: 0.4 59 | maximumLODLevel: 0 60 | particleRaycastBudget: 16 61 | asyncUploadTimeSlice: 2 62 | asyncUploadBufferSize: 16 63 | resolutionScalingFixedDPIFactor: 1 64 | excludedTargetPlatforms: [] 65 | - serializedVersion: 2 66 | name: Medium 67 | pixelLightCount: 1 68 | shadows: 0 69 | shadowResolution: 0 70 | shadowProjection: 1 71 | shadowCascades: 1 72 | shadowDistance: 20 73 | shadowNearPlaneOffset: 3 74 | shadowCascade2Split: 0.33333334 75 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 76 | shadowmaskMode: 0 77 | blendWeights: 2 78 | textureQuality: 0 79 | anisotropicTextures: 0 80 | antiAliasing: 0 81 | softParticles: 0 82 | softVegetation: 0 83 | realtimeReflectionProbes: 0 84 | billboardsFaceCameraPosition: 0 85 | vSyncCount: 1 86 | lodBias: 0.7 87 | maximumLODLevel: 0 88 | particleRaycastBudget: 64 89 | asyncUploadTimeSlice: 2 90 | asyncUploadBufferSize: 16 91 | resolutionScalingFixedDPIFactor: 1 92 | excludedTargetPlatforms: [] 93 | - serializedVersion: 2 94 | name: High 95 | pixelLightCount: 2 96 | shadows: 0 97 | shadowResolution: 1 98 | shadowProjection: 1 99 | shadowCascades: 2 100 | shadowDistance: 40 101 | shadowNearPlaneOffset: 3 102 | shadowCascade2Split: 0.33333334 103 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 104 | shadowmaskMode: 1 105 | blendWeights: 2 106 | textureQuality: 0 107 | anisotropicTextures: 0 108 | antiAliasing: 0 109 | softParticles: 0 110 | softVegetation: 1 111 | realtimeReflectionProbes: 0 112 | billboardsFaceCameraPosition: 0 113 | vSyncCount: 1 114 | lodBias: 1 115 | maximumLODLevel: 0 116 | particleRaycastBudget: 256 117 | asyncUploadTimeSlice: 2 118 | asyncUploadBufferSize: 16 119 | resolutionScalingFixedDPIFactor: 1 120 | excludedTargetPlatforms: [] 121 | - serializedVersion: 2 122 | name: Very High 123 | pixelLightCount: 3 124 | shadows: 0 125 | shadowResolution: 2 126 | shadowProjection: 1 127 | shadowCascades: 2 128 | shadowDistance: 70 129 | shadowNearPlaneOffset: 3 130 | shadowCascade2Split: 0.33333334 131 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 132 | shadowmaskMode: 1 133 | blendWeights: 4 134 | textureQuality: 0 135 | anisotropicTextures: 0 136 | antiAliasing: 0 137 | softParticles: 0 138 | softVegetation: 1 139 | realtimeReflectionProbes: 0 140 | billboardsFaceCameraPosition: 0 141 | vSyncCount: 1 142 | lodBias: 1.5 143 | maximumLODLevel: 0 144 | particleRaycastBudget: 1024 145 | asyncUploadTimeSlice: 2 146 | asyncUploadBufferSize: 16 147 | resolutionScalingFixedDPIFactor: 1 148 | excludedTargetPlatforms: [] 149 | - serializedVersion: 2 150 | name: Ultra 151 | pixelLightCount: 4 152 | shadows: 0 153 | shadowResolution: 0 154 | shadowProjection: 1 155 | shadowCascades: 4 156 | shadowDistance: 150 157 | shadowNearPlaneOffset: 3 158 | shadowCascade2Split: 0.33333334 159 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 160 | shadowmaskMode: 1 161 | blendWeights: 4 162 | textureQuality: 0 163 | anisotropicTextures: 0 164 | antiAliasing: 0 165 | softParticles: 0 166 | softVegetation: 1 167 | realtimeReflectionProbes: 0 168 | billboardsFaceCameraPosition: 0 169 | vSyncCount: 1 170 | lodBias: 2 171 | maximumLODLevel: 0 172 | particleRaycastBudget: 4096 173 | asyncUploadTimeSlice: 2 174 | asyncUploadBufferSize: 16 175 | resolutionScalingFixedDPIFactor: 1 176 | excludedTargetPlatforms: [] 177 | m_PerPlatformDefaultQuality: 178 | Android: 2 179 | Nintendo 3DS: 5 180 | Nintendo Switch: 5 181 | PS4: 5 182 | PSM: 5 183 | PSP2: 2 184 | Standalone: 5 185 | Tizen: 2 186 | WebGL: 3 187 | WiiU: 5 188 | Windows Store Apps: 5 189 | XboxOne: 5 190 | iPhone: 2 191 | tvOS: 2 192 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/SceneTemplateSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "templatePinStates": [], 3 | "dependencyTypeInfos": [ 4 | { 5 | "userAdded": false, 6 | "type": "UnityEngine.AnimationClip", 7 | "defaultInstantiationMode": 0 8 | }, 9 | { 10 | "userAdded": false, 11 | "type": "UnityEditor.Animations.AnimatorController", 12 | "defaultInstantiationMode": 0 13 | }, 14 | { 15 | "userAdded": false, 16 | "type": "UnityEngine.AnimatorOverrideController", 17 | "defaultInstantiationMode": 0 18 | }, 19 | { 20 | "userAdded": false, 21 | "type": "UnityEditor.Audio.AudioMixerController", 22 | "defaultInstantiationMode": 0 23 | }, 24 | { 25 | "userAdded": false, 26 | "type": "UnityEngine.ComputeShader", 27 | "defaultInstantiationMode": 1 28 | }, 29 | { 30 | "userAdded": false, 31 | "type": "UnityEngine.Cubemap", 32 | "defaultInstantiationMode": 0 33 | }, 34 | { 35 | "userAdded": false, 36 | "type": "UnityEngine.GameObject", 37 | "defaultInstantiationMode": 0 38 | }, 39 | { 40 | "userAdded": false, 41 | "type": "UnityEditor.LightingDataAsset", 42 | "defaultInstantiationMode": 0 43 | }, 44 | { 45 | "userAdded": false, 46 | "type": "UnityEngine.LightingSettings", 47 | "defaultInstantiationMode": 0 48 | }, 49 | { 50 | "userAdded": false, 51 | "type": "UnityEngine.Material", 52 | "defaultInstantiationMode": 0 53 | }, 54 | { 55 | "userAdded": false, 56 | "type": "UnityEditor.MonoScript", 57 | "defaultInstantiationMode": 1 58 | }, 59 | { 60 | "userAdded": false, 61 | "type": "UnityEngine.PhysicMaterial", 62 | "defaultInstantiationMode": 0 63 | }, 64 | { 65 | "userAdded": false, 66 | "type": "UnityEngine.PhysicsMaterial2D", 67 | "defaultInstantiationMode": 0 68 | }, 69 | { 70 | "userAdded": false, 71 | "type": "UnityEngine.Rendering.PostProcessing.PostProcessProfile", 72 | "defaultInstantiationMode": 0 73 | }, 74 | { 75 | "userAdded": false, 76 | "type": "UnityEngine.Rendering.PostProcessing.PostProcessResources", 77 | "defaultInstantiationMode": 0 78 | }, 79 | { 80 | "userAdded": false, 81 | "type": "UnityEngine.Rendering.VolumeProfile", 82 | "defaultInstantiationMode": 0 83 | }, 84 | { 85 | "userAdded": false, 86 | "type": "UnityEditor.SceneAsset", 87 | "defaultInstantiationMode": 1 88 | }, 89 | { 90 | "userAdded": false, 91 | "type": "UnityEngine.Shader", 92 | "defaultInstantiationMode": 1 93 | }, 94 | { 95 | "userAdded": false, 96 | "type": "UnityEngine.ShaderVariantCollection", 97 | "defaultInstantiationMode": 1 98 | }, 99 | { 100 | "userAdded": false, 101 | "type": "UnityEngine.Texture", 102 | "defaultInstantiationMode": 0 103 | }, 104 | { 105 | "userAdded": false, 106 | "type": "UnityEngine.Texture2D", 107 | "defaultInstantiationMode": 0 108 | }, 109 | { 110 | "userAdded": false, 111 | "type": "UnityEngine.Timeline.TimelineAsset", 112 | "defaultInstantiationMode": 0 113 | } 114 | ], 115 | "defaultDependencyTypeInfo": { 116 | "userAdded": false, 117 | "type": "", 118 | "defaultInstantiationMode": 1 119 | }, 120 | "newSceneOverride": 0 121 | } -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!78 &1 4 | TagManager: 5 | serializedVersion: 2 6 | tags: [] 7 | layers: 8 | - Default 9 | - TransparentFX 10 | - Ignore Raycast 11 | - 12 | - Water 13 | - UI 14 | - 15 | - 16 | - 17 | - 18 | - 19 | - 20 | - 21 | - 22 | - 23 | - 24 | - 25 | - 26 | - 27 | - 28 | - 29 | - 30 | - 31 | - 32 | - 33 | - 34 | - 35 | - 36 | - 37 | - 38 | - 39 | - 40 | m_SortingLayers: 41 | - name: Default 42 | uniqueID: 0 43 | locked: 0 44 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!5 &1 4 | TimeManager: 5 | m_ObjectHideFlags: 0 6 | Fixed Timestep: 0.02 7 | Maximum Allowed Timestep: 0.1 8 | m_TimeScale: 1 9 | Maximum Particle Timestep: 0.03 10 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/UnityConnectSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!310 &1 4 | UnityConnectSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 1 7 | m_Enabled: 0 8 | m_TestMode: 0 9 | m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events 10 | m_EventUrl: https://cdp.cloud.unity3d.com/v1/events 11 | m_ConfigUrl: https://config.uca.cloud.unity3d.com 12 | m_DashboardUrl: https://dashboard.unity3d.com 13 | m_TestInitMode: 0 14 | CrashReportingSettings: 15 | m_EventUrl: https://perf-events.cloud.unity3d.com 16 | m_Enabled: 0 17 | m_LogBufferSize: 10 18 | m_CaptureEditorExceptions: 1 19 | UnityPurchasingSettings: 20 | m_Enabled: 0 21 | m_TestMode: 0 22 | UnityAnalyticsSettings: 23 | m_Enabled: 0 24 | m_TestMode: 0 25 | m_InitializeOnStartup: 1 26 | m_PackageRequiringCoreStatsPresent: 0 27 | UnityAdsSettings: 28 | m_Enabled: 0 29 | m_InitializeOnStartup: 1 30 | m_TestMode: 0 31 | m_IosGameId: 32 | m_AndroidGameId: 33 | m_GameIds: {} 34 | m_GameId: 35 | PerformanceReportingSettings: 36 | m_Enabled: 0 37 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/VFXManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!937362698 &1 4 | VFXManager: 5 | m_ObjectHideFlags: 0 6 | m_IndirectShader: {fileID: 0} 7 | m_RenderPipeSettingsPath: 8 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/VersionControlSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!890905787 &1 4 | VersionControlSettings: 5 | m_ObjectHideFlags: 0 6 | m_Mode: Visible Meta Files 7 | m_CollabEditorSettings: 8 | inProgressEnabled: 1 9 | -------------------------------------------------------------------------------- /src/MasterMemory.Unity/ProjectSettings/XRSettings.asset: -------------------------------------------------------------------------------- 1 | { 2 | "m_SettingKeys": [ 3 | "VR Device Disabled", 4 | "VR Device User Alert" 5 | ], 6 | "m_SettingValues": [ 7 | "False", 8 | "False" 9 | ] 10 | } -------------------------------------------------------------------------------- /src/MasterMemory/DatabaseBuilderBase.cs: -------------------------------------------------------------------------------- 1 | using MasterMemory.Internal; 2 | using MessagePack; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Reflection; 7 | using System.Runtime.InteropServices; 8 | 9 | namespace MasterMemory 10 | { 11 | public abstract class DatabaseBuilderBase 12 | { 13 | readonly ByteBufferWriter bufferWriter = new ByteBufferWriter(); 14 | 15 | // TableName, (Offset, Count) 16 | readonly Dictionary header = new Dictionary(); 17 | readonly MessagePackSerializerOptions? options; 18 | 19 | public DatabaseBuilderBase(MessagePackSerializerOptions? options) 20 | { 21 | // options keep null to lazily use default options 22 | if (options != null) 23 | { 24 | options = options.WithCompression(MessagePackCompression.Lz4Block); 25 | } 26 | } 27 | 28 | public DatabaseBuilderBase(IFormatterResolver? resolver) 29 | { 30 | if (resolver != null) 31 | { 32 | this.options = MessagePackSerializer.DefaultOptions 33 | .WithCompression(MessagePackCompression.Lz4Block) 34 | .WithResolver(resolver); 35 | } 36 | 37 | } 38 | 39 | protected void AppendCore(IEnumerable datasource, Func indexSelector, IComparer comparer) 40 | { 41 | var tableName = typeof(T).GetCustomAttribute(); 42 | if (tableName == null) throw new InvalidOperationException("Type is not annotated MemoryTableAttribute. Type:" + typeof(T).FullName); 43 | 44 | if (header.ContainsKey(tableName.TableName)) 45 | { 46 | throw new InvalidOperationException("TableName is already appended in builder. TableName: " + tableName.TableName + " Type:" + typeof(T).FullName); 47 | } 48 | 49 | if (datasource == null) return; 50 | 51 | // sort(as indexed data-table) 52 | var source = FastSort(datasource, indexSelector, comparer); 53 | 54 | // write data and store header-data. 55 | var useOption = options ?? MessagePackSerializer.DefaultOptions.WithCompression(MessagePackCompression.Lz4Block); 56 | 57 | var offset = bufferWriter.CurrentOffset; 58 | MessagePackSerializer.Serialize(bufferWriter, source, useOption); 59 | 60 | header.Add(tableName.TableName, (offset, bufferWriter.CurrentOffset - offset)); 61 | } 62 | 63 | static TElement[] FastSort(IEnumerable datasource, Func indexSelector, IComparer comparer) 64 | { 65 | var collection = datasource as ICollection; 66 | if (collection != null) 67 | { 68 | var array = new TElement[collection.Count]; 69 | var sortSource = new TKey[collection.Count]; 70 | var i = 0; 71 | foreach (var item in collection) 72 | { 73 | array[i] = item; 74 | sortSource[i] = indexSelector(item); 75 | i++; 76 | } 77 | Array.Sort(sortSource, array, 0, collection.Count, comparer); 78 | return array; 79 | } 80 | else 81 | { 82 | var array = new ExpandableArray(null!); 83 | var sortSource = new ExpandableArray(null!); 84 | foreach (var item in datasource) 85 | { 86 | array.Add(item); 87 | sortSource.Add(indexSelector(item)); 88 | } 89 | 90 | Array.Sort(sortSource.items, array.items, 0, array.count, comparer); 91 | 92 | Array.Resize(ref array.items, array.count); 93 | return array.items; 94 | } 95 | } 96 | 97 | public byte[] Build() 98 | { 99 | using (var ms = new MemoryStream()) 100 | { 101 | WriteToStream(ms); 102 | return ms.ToArray(); 103 | } 104 | } 105 | 106 | public void WriteToStream(Stream stream) 107 | { 108 | MessagePackSerializer.Serialize(stream, header, HeaderFormatterResolver.StandardOptions); 109 | MemoryMarshal.TryGetArray(bufferWriter.WrittenMemory, out var segment); 110 | stream.Write(segment.Array, segment.Offset, segment.Count); 111 | } 112 | } 113 | } -------------------------------------------------------------------------------- /src/MasterMemory/DatabaseBuilderBaseExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace MasterMemory 6 | { 7 | public static class DatabaseBuilderExtensions 8 | { 9 | public static void AppendDynamic(this DatabaseBuilderBase builder, Type dataType, IList tableData) 10 | { 11 | var appendMethod = builder.GetType().GetMethods() 12 | .Where(x => x.Name == "Append") 13 | .Where(x => x.GetParameters()[0].ParameterType.GetGenericArguments()[0] == dataType) 14 | .FirstOrDefault(); 15 | 16 | if (appendMethod == null) 17 | { 18 | throw new InvalidOperationException("Append(IEnumerable) can not found. DataType:" + dataType); 19 | } 20 | 21 | var dynamicArray = Array.CreateInstance(dataType, tableData.Count); 22 | for (int i = 0; i < tableData.Count; i++) 23 | { 24 | dynamicArray.SetValue(Convert.ChangeType(tableData[i], dataType), i); 25 | } 26 | 27 | appendMethod.Invoke(builder, new object[] { dynamicArray }); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/MasterMemory/IValidatable.cs: -------------------------------------------------------------------------------- 1 | using MasterMemory.Validation; 2 | using System; 3 | using System.Linq.Expressions; 4 | 5 | namespace MasterMemory 6 | { 7 | public interface IValidatable 8 | { 9 | void Validate(IValidator validator); 10 | } 11 | 12 | public interface IValidator 13 | { 14 | ValidatableSet GetTableSet(); 15 | ReferenceSet GetReferenceSet(); 16 | void Validate(Expression> predicate); 17 | void Validate(Func predicate, string message); 18 | void ValidateAction(Expression> predicate); 19 | void ValidateAction(Func predicate, string message); 20 | void Fail(string message); 21 | bool CallOnce(); 22 | } 23 | } -------------------------------------------------------------------------------- /src/MasterMemory/ImmutableBuilderBase.cs: -------------------------------------------------------------------------------- 1 | using MasterMemory.Internal; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace MasterMemory 6 | { 7 | public abstract class ImmutableBuilderBase 8 | { 9 | static protected TElement[] CloneAndSortBy(IList elementData, Func indexSelector, IComparer comparer) 10 | { 11 | var array = new TElement[elementData.Count]; 12 | var sortSource = new TKey[elementData.Count]; 13 | for (int i = 0; i < elementData.Count; i++) 14 | { 15 | array[i] = elementData[i]; 16 | sortSource[i] = indexSelector(elementData[i]); 17 | } 18 | 19 | Array.Sort(sortSource, array, 0, array.Length, comparer); 20 | return array; 21 | } 22 | 23 | static protected List RemoveCore(TElement[] array, TKey[] keys, Func keySelector, IComparer comparer) 24 | { 25 | var removeIndexes = new HashSet(); 26 | foreach (var key in keys) 27 | { 28 | var index = BinarySearch.FindFirst(array, key, keySelector, comparer); 29 | if (index != -1) 30 | { 31 | removeIndexes.Add(index); 32 | } 33 | } 34 | 35 | var newList = new List(array.Length - removeIndexes.Count); 36 | for (int i = 0; i < array.Length; i++) 37 | { 38 | if (!removeIndexes.Contains(i)) 39 | { 40 | newList.Add(array[i]); 41 | } 42 | } 43 | 44 | return newList; 45 | } 46 | 47 | static protected List DiffCore(TElement[] array, TElement[] addOrReplaceData, Func keySelector, IComparer comparer) 48 | { 49 | var newList = new List(array.Length); 50 | var replaceIndexes = new Dictionary(); 51 | foreach (var data in addOrReplaceData) 52 | { 53 | var index = BinarySearch.FindFirst(array, keySelector(data), keySelector, comparer); 54 | if (index != -1) 55 | { 56 | replaceIndexes.Add(index, data); 57 | } 58 | else 59 | { 60 | newList.Add(data); 61 | } 62 | } 63 | 64 | for (int i = 0; i < array.Length; i++) 65 | { 66 | if (replaceIndexes.TryGetValue(i, out var data)) 67 | { 68 | newList.Add(data); 69 | } 70 | else 71 | { 72 | newList.Add(array[i]); 73 | } 74 | } 75 | 76 | return newList; 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /src/MasterMemory/Internal/ByteBufferWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | 4 | namespace MasterMemory 5 | { 6 | internal class ByteBufferWriter : IBufferWriter 7 | { 8 | byte[] buffer; 9 | int index; 10 | 11 | public int CurrentOffset => index; 12 | public ReadOnlySpan WrittenSpan => buffer.AsSpan(0, index); 13 | public ReadOnlyMemory WrittenMemory => new ReadOnlyMemory(buffer, 0, index); 14 | 15 | public ByteBufferWriter() 16 | { 17 | buffer = new byte[1024]; 18 | index = 0; 19 | } 20 | 21 | public void Advance(int count) 22 | { 23 | index += count; 24 | } 25 | 26 | public Memory GetMemory(int sizeHint = 0) 27 | { 28 | AGAIN: 29 | var nextSize = index + sizeHint; 30 | if (buffer.Length < nextSize) 31 | { 32 | Array.Resize(ref buffer, Math.Max(buffer.Length * 2, nextSize)); 33 | } 34 | 35 | if (sizeHint == 0) 36 | { 37 | var result = new Memory(buffer, index, buffer.Length - index); 38 | if (result.Length == 0) 39 | { 40 | sizeHint = 1024; 41 | goto AGAIN; 42 | } 43 | return result; 44 | } 45 | else 46 | { 47 | return new Memory(buffer, index, sizeHint); 48 | } 49 | } 50 | 51 | public Span GetSpan(int sizeHint = 0) 52 | { 53 | return GetMemory(sizeHint).Span; 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/MasterMemory/Internal/ExpandableArray.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MasterMemory.Internal 4 | { 5 | internal struct ExpandableArray 6 | { 7 | internal TElement[] items; 8 | internal int count; 9 | 10 | public ExpandableArray(object dummy) 11 | { 12 | items = Array.Empty(); 13 | count = 0; 14 | } 15 | 16 | internal void Add(TElement item) 17 | { 18 | if (items == null || items.Length == 0) 19 | { 20 | items = new TElement[4]; 21 | } 22 | else if (items.Length == (count + 1)) 23 | { 24 | Array.Resize(ref items, checked(count * 2)); 25 | } 26 | items[count++] = item; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/MasterMemory/Internal/HeaderFormatterResolver.cs: -------------------------------------------------------------------------------- 1 | using MessagePack; 2 | using MessagePack.Formatters; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace MasterMemory.Internal 7 | { 8 | // for AOT(IL2CPP) concrete generic formatter. 9 | internal class HeaderFormatterResolver : IFormatterResolver 10 | { 11 | public static readonly IFormatterResolver Instance = new HeaderFormatterResolver(); 12 | public static readonly MessagePackSerializerOptions StandardOptions = MessagePackSerializerOptions.Standard.WithResolver(Instance); 13 | 14 | public IMessagePackFormatter? GetFormatter() 15 | { 16 | if (typeof(T) == typeof(Dictionary)) 17 | { 18 | return (IMessagePackFormatter)(object)new DictionaryFormatter(); 19 | } 20 | else if (typeof(T) == typeof(string)) 21 | { 22 | return (IMessagePackFormatter)(object)NullableStringFormatter.Instance; 23 | } 24 | else if (typeof(T) == typeof((int, int))) 25 | { 26 | return (IMessagePackFormatter)(object)new IntIntValueTupleFormatter(); 27 | } 28 | else if (typeof(T) == typeof(int)) 29 | { 30 | return (IMessagePackFormatter)(object)Int32Formatter.Instance; 31 | } 32 | 33 | return null; 34 | } 35 | } 36 | 37 | internal sealed class IntIntValueTupleFormatter : IMessagePackFormatter> 38 | { 39 | public void Serialize(ref MessagePackWriter writer, (int, int) value, MessagePackSerializerOptions options) 40 | { 41 | writer.WriteArrayHeader(2); 42 | writer.WriteInt32(value.Item1); 43 | writer.WriteInt32(value.Item2); 44 | } 45 | 46 | public (int, int) Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) 47 | { 48 | if (reader.IsNil) 49 | { 50 | throw new InvalidOperationException("Data is Nil, ValueTuple can not be null."); 51 | } 52 | 53 | var count = reader.ReadArrayHeader(); 54 | if (count != 2) throw new InvalidOperationException("Invalid ValueTuple count"); 55 | 56 | var item1 = reader.ReadInt32(); 57 | var item2 = reader.ReadInt32(); 58 | 59 | return new ValueTuple(item1, item2); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /src/MasterMemory/Internal/InternStringResolver.cs: -------------------------------------------------------------------------------- 1 | using MessagePack; 2 | using MessagePack.Formatters; 3 | using System; 4 | 5 | namespace MasterMemory.Internal 6 | { 7 | #pragma warning disable MsgPack013 // Inaccessible formatter 8 | internal class InternStringResolver : IFormatterResolver, IMessagePackFormatter 9 | { 10 | readonly IFormatterResolver innerResolver; 11 | 12 | public InternStringResolver(IFormatterResolver innerResolver) 13 | { 14 | this.innerResolver = innerResolver; 15 | } 16 | 17 | public IMessagePackFormatter? GetFormatter() 18 | { 19 | if (typeof(T) == typeof(string)) 20 | { 21 | return (IMessagePackFormatter)this; 22 | } 23 | 24 | return innerResolver.GetFormatter(); 25 | } 26 | 27 | string? IMessagePackFormatter.Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) 28 | { 29 | var str = reader.ReadString(); 30 | if (str == null) 31 | { 32 | return null; 33 | } 34 | 35 | return string.Intern(str); 36 | } 37 | 38 | void IMessagePackFormatter.Serialize(ref MessagePackWriter writer, string? value, MessagePackSerializerOptions options) 39 | { 40 | throw new NotImplementedException(); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/MasterMemory/MasterMemory.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | MasterMemory 6 | MasterMemory 7 | 13 8 | enable 9 | Library 10 | False 11 | Cysharp 12 | true 13 | 1701;1702;1705;1591 14 | 15 | 16 | MasterMemory 17 | $(Version) 18 | Cysharp 19 | Cysharp 20 | Embedded Typed Readonly In-Memory Document Database for .NET Core and Unity. 21 | https://github.com/Cysharp/MasterMemory 22 | $(PackageProjectUrl) 23 | git 24 | database, embedded, inmemory, unity 25 | true 26 | 27 | 28 | $(TargetsForTfmSpecificContentInPackage);PackBuildOutputs 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 48 | 50 | 51 | 52 | 53 | 54 | TextTemplatingFileGenerator 55 | ValidatableSet.Sequential.cs 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | True 64 | True 65 | ValidatableSet.Sequential.tt 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/MasterMemory/MemoryDatabaseBase.cs: -------------------------------------------------------------------------------- 1 | using MasterMemory.Internal; 2 | using MessagePack; 3 | using MessagePack.Formatters; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Reflection; 7 | using System.Linq; 8 | using System.Buffers; 9 | using MasterMemory.Validation; 10 | 11 | namespace MasterMemory 12 | { 13 | public abstract class MemoryDatabaseBase 14 | { 15 | protected MemoryDatabaseBase() 16 | { 17 | 18 | } 19 | 20 | public MemoryDatabaseBase(byte[] databaseBinary, bool internString = true, IFormatterResolver? formatterResolver = null, int maxDegreeOfParallelism = 1) 21 | { 22 | var reader = new MessagePackReader(databaseBinary); 23 | var formatter = new DictionaryFormatter(); 24 | 25 | var header = formatter.Deserialize(ref reader, HeaderFormatterResolver.StandardOptions); 26 | var resolver = formatterResolver ?? MessagePackSerializer.DefaultOptions.Resolver; 27 | if (internString) 28 | { 29 | resolver = new InternStringResolver(resolver); 30 | } 31 | if (maxDegreeOfParallelism < 1) 32 | { 33 | maxDegreeOfParallelism = 1; 34 | } 35 | 36 | Init(header!, databaseBinary.AsMemory((int)reader.Consumed), MessagePackSerializer.DefaultOptions.WithResolver(resolver).WithCompression(MessagePackCompression.Lz4Block), maxDegreeOfParallelism); 37 | } 38 | 39 | protected static TView ExtractTableData(Dictionary header, ReadOnlyMemory databaseBinary, MessagePackSerializerOptions options, Func createView) 40 | { 41 | var tableName = typeof(T).GetCustomAttribute(); 42 | if (tableName == null) throw new InvalidOperationException("Type is not annotated MemoryTableAttribute. Type:" + typeof(T).FullName); 43 | 44 | if (header.TryGetValue(tableName.TableName, out var segment)) 45 | { 46 | var data = MessagePackSerializer.Deserialize(databaseBinary.Slice(segment.offset, segment.count), options); 47 | return createView(data); 48 | } 49 | else 50 | { 51 | // return empty 52 | var data = Array.Empty(); 53 | return createView(data); 54 | } 55 | } 56 | 57 | protected abstract void Init(Dictionary header, ReadOnlyMemory databaseBinary, MessagePackSerializerOptions options, int maxDegreeOfParallelism); 58 | 59 | public static TableInfo[] GetTableInfo(byte[] databaseBinary, bool storeTableData = true) 60 | { 61 | var formatter = new DictionaryFormatter(); 62 | var reader = new MessagePackReader(databaseBinary); 63 | var header = formatter.Deserialize(ref reader, HeaderFormatterResolver.StandardOptions); 64 | 65 | return header.Select(x => new TableInfo(x.Key, x.Value.Item2, storeTableData ? databaseBinary : null, x.Value.Item1)).ToArray(); 66 | } 67 | 68 | protected void ValidateTable(IReadOnlyList table, ValidationDatabase database, string pkName, Delegate pkSelector, ValidateResult result) 69 | { 70 | var onceCalled = new System.Runtime.CompilerServices.StrongBox(false); 71 | foreach (var item in table) 72 | { 73 | if (item is IValidatable validatable) 74 | { 75 | var validator = new Validator(database, item, result, onceCalled, pkName, pkSelector); 76 | validatable.Validate(validator); 77 | } 78 | } 79 | } 80 | } 81 | 82 | /// 83 | /// Diagnostic info of MasterMemory's table. 84 | /// 85 | public class TableInfo 86 | { 87 | public string TableName { get; } 88 | public int Size { get; } 89 | byte[]? binaryData; 90 | 91 | public TableInfo(string tableName, int size, byte[]? rawBinary, int offset) 92 | { 93 | TableName = tableName; 94 | Size = size; 95 | if (rawBinary != null) 96 | { 97 | this.binaryData = new byte[size]; 98 | Array.Copy(rawBinary, offset, binaryData, 0, size); 99 | } 100 | } 101 | 102 | public string DumpAsJson() 103 | { 104 | return DumpAsJson(MessagePackSerializer.DefaultOptions); 105 | } 106 | 107 | public string DumpAsJson(MessagePackSerializerOptions options) 108 | { 109 | if (binaryData == null) 110 | { 111 | throw new InvalidOperationException("DumpAsJson can only call from GetTableInfo(storeTableData = true)."); 112 | } 113 | 114 | return MessagePackSerializer.ConvertToJson(binaryData, options.WithCompression(MessagePackCompression.Lz4Block)); 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /src/MasterMemory/Meta/Meta.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | using System.Text; 6 | 7 | namespace MasterMemory.Meta 8 | { 9 | public class MetaDatabase 10 | { 11 | IDictionary tableInfos; 12 | 13 | public MetaDatabase(IDictionary tableInfos) 14 | { 15 | this.tableInfos = tableInfos; 16 | } 17 | 18 | public int Count => tableInfos.Count; 19 | 20 | public IEnumerable GetTableInfos() 21 | { 22 | foreach (var item in tableInfos.Values) 23 | { 24 | yield return item; 25 | } 26 | } 27 | 28 | public MetaTable? GetTableInfo(string tableName) 29 | { 30 | return tableInfos.TryGetValue(tableName, out var table) 31 | ? table 32 | : null; 33 | } 34 | } 35 | 36 | public class MetaTable 37 | { 38 | public Type DataType { get; } 39 | public Type TableType { get; } 40 | public string TableName { get; } 41 | public IReadOnlyList Properties { get; } 42 | public IReadOnlyList Indexes { get; } 43 | 44 | public MetaTable(Type dataType, Type tableType, string tableName, IReadOnlyList properties, IReadOnlyList Indexes) 45 | { 46 | this.DataType = dataType; 47 | this.TableType = tableType; 48 | this.TableName = tableName; 49 | this.Properties = properties; 50 | this.Indexes = Indexes; 51 | } 52 | 53 | public override string ToString() 54 | { 55 | return TableName; 56 | } 57 | } 58 | 59 | public class MetaProperty 60 | { 61 | public PropertyInfo PropertyInfo { get; } 62 | 63 | public string Name => PropertyInfo.Name; 64 | public string NameLowerCamel => ToCamelCase(PropertyInfo.Name); 65 | public string NameSnakeCase => ToSnakeCase(PropertyInfo.Name); 66 | 67 | public MetaProperty(PropertyInfo? propertyInfo) 68 | { 69 | PropertyInfo = propertyInfo!; 70 | } 71 | 72 | public override string ToString() 73 | { 74 | return Name; 75 | } 76 | 77 | /// 78 | /// MyProperty -> myProperty 79 | /// 80 | static string ToCamelCase(string s) 81 | { 82 | if (string.IsNullOrEmpty(s) || char.IsLower(s, 0)) 83 | { 84 | return s; 85 | } 86 | 87 | var array = s.ToCharArray(); 88 | array[0] = char.ToLowerInvariant(array[0]); 89 | return new string(array); 90 | } 91 | 92 | /// 93 | /// MyProperty -> my_property 94 | /// 95 | static string ToSnakeCase(string s) 96 | { 97 | if (string.IsNullOrEmpty(s)) return s; 98 | 99 | var sb = new StringBuilder(); 100 | for (int i = 0; i < s.Length; i++) 101 | { 102 | var c = s[i]; 103 | 104 | if (Char.IsUpper(c)) 105 | { 106 | // first 107 | if (i == 0) 108 | { 109 | sb.Append(char.ToLowerInvariant(c)); 110 | } 111 | else if (char.IsUpper(s[i - 1])) // WriteIO => write_io 112 | { 113 | sb.Append(char.ToLowerInvariant(c)); 114 | } 115 | else 116 | { 117 | sb.Append("_"); 118 | sb.Append(char.ToLowerInvariant(c)); 119 | } 120 | } 121 | else 122 | { 123 | sb.Append(c); 124 | } 125 | } 126 | 127 | return sb.ToString(); 128 | } 129 | } 130 | 131 | public class MetaIndex 132 | { 133 | public IReadOnlyList IndexProperties { get; } 134 | public bool IsPrimaryIndex { get; } 135 | public bool IsUnique { get; } 136 | public System.Collections.IComparer Comparer { get; } 137 | public bool IsReturnRangeValue => IndexProperties.Count != 1; 138 | 139 | public MetaIndex(IReadOnlyList indexProperties, bool isPrimaryIndex, bool isUnique, System.Collections.IComparer comparer) 140 | { 141 | IndexProperties = indexProperties; 142 | IsPrimaryIndex = isPrimaryIndex; 143 | IsUnique = isUnique; 144 | Comparer = comparer; 145 | } 146 | 147 | public override string ToString() 148 | { 149 | return string.Join(", ", IndexProperties.Select(x => x.Name)); 150 | } 151 | } 152 | } -------------------------------------------------------------------------------- /src/MasterMemory/RangeView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace MasterMemory 6 | { 7 | public readonly struct RangeView : IEnumerable, IReadOnlyList, IList 8 | { 9 | public static RangeView Empty => new RangeView(null, -1, -1, false); 10 | 11 | readonly T[]? orderedData; 12 | readonly int left; 13 | readonly int right; 14 | readonly bool ascendant; 15 | readonly bool hasValue; 16 | 17 | public int Count => (!hasValue) ? 0 : (right - left) + 1; 18 | public T First => this[0]; 19 | public T Last => this[Count - 1]; 20 | 21 | public RangeView Reverse => new RangeView(orderedData, left, right, !ascendant); 22 | 23 | internal int FirstIndex => ascendant ? left : right; 24 | internal int LastIndex => ascendant ? right : left; 25 | 26 | bool ICollection.IsReadOnly => true; 27 | 28 | public T this[int index] 29 | { 30 | get 31 | { 32 | if (!hasValue) throw new ArgumentOutOfRangeException("view is empty"); 33 | if (index < 0) throw new ArgumentOutOfRangeException("index < 0"); 34 | if (Count <= index) throw new ArgumentOutOfRangeException("count <= index"); 35 | 36 | if (ascendant) 37 | { 38 | return orderedData![left + index]; 39 | } 40 | else 41 | { 42 | return orderedData![right - index]; 43 | } 44 | } 45 | } 46 | 47 | public RangeView(T[]? orderedData, int left, int right, bool ascendant) 48 | { 49 | this.hasValue = (orderedData != null) && (orderedData.Length != 0) && (left <= right); // same index is length = 1 this.orderedData = orderedData; 50 | this.orderedData = orderedData; 51 | this.left = left; 52 | this.right = right; 53 | this.ascendant = ascendant; 54 | } 55 | 56 | public IEnumerator GetEnumerator() 57 | { 58 | var count = Count; 59 | for (int i = 0; i < count; i++) 60 | { 61 | yield return this[i]; 62 | } 63 | } 64 | 65 | IEnumerator IEnumerable.GetEnumerator() 66 | { 67 | return GetEnumerator(); 68 | } 69 | 70 | public bool Any() 71 | { 72 | return Count != 0; 73 | } 74 | 75 | public int IndexOf(T item) 76 | { 77 | var i = 0; 78 | foreach (var v in this) 79 | { 80 | if (EqualityComparer.Default.Equals(v, item)) 81 | { 82 | return i; 83 | } 84 | i++; 85 | } 86 | return -1; 87 | } 88 | 89 | public bool Contains(T item) 90 | { 91 | var count = Count; 92 | for (int i = 0; i < count; i++) 93 | { 94 | var v = this[i]; 95 | if (EqualityComparer.Default.Equals(v, item)) 96 | { 97 | return true; 98 | } 99 | } 100 | return false; 101 | } 102 | 103 | public void CopyTo(T[] array, int arrayIndex) 104 | { 105 | var count = Count; 106 | Array.Copy(orderedData, left, array, arrayIndex, count); 107 | if (!ascendant) 108 | { 109 | Array.Reverse(array, arrayIndex, count); 110 | } 111 | } 112 | 113 | T IList.this[int index] 114 | { 115 | get 116 | { 117 | return this[index]; 118 | } 119 | set 120 | { 121 | throw new NotImplementedException(); 122 | } 123 | } 124 | 125 | void IList.Insert(int index, T item) 126 | { 127 | throw new NotSupportedException(); 128 | } 129 | 130 | void IList.RemoveAt(int index) 131 | { 132 | throw new NotSupportedException(); 133 | } 134 | 135 | void ICollection.Add(T item) 136 | { 137 | throw new NotSupportedException(); 138 | } 139 | 140 | void ICollection.Clear() 141 | { 142 | throw new NotSupportedException(); 143 | } 144 | 145 | bool ICollection.Remove(T item) 146 | { 147 | throw new NotSupportedException(); 148 | } 149 | } 150 | } -------------------------------------------------------------------------------- /src/MasterMemory/Validation/ExpressionDumper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Text; 7 | 8 | namespace MasterMemory.Validation 9 | { 10 | internal class ExpressionDumper : ExpressionVisitor 11 | { 12 | ParameterExpression param; 13 | T target; 14 | 15 | public Dictionary Members { get; private set; } 16 | 17 | public ExpressionDumper(T target, ParameterExpression param) 18 | { 19 | this.target = target; 20 | this.param = param; 21 | this.Members = new Dictionary(); 22 | } 23 | 24 | protected override System.Linq.Expressions.Expression VisitMember(MemberExpression node) 25 | { 26 | if (node.Expression == param && !Members.ContainsKey(node.Member.Name)) 27 | { 28 | var accessor = new ReflectAccessor(target, node.Member.Name); 29 | Members.Add(node.Member.Name, accessor.GetValue()); 30 | } 31 | 32 | return base.VisitMember(node); 33 | } 34 | 35 | public static string DumpMemberValues(T item, Expression> predicate) 36 | { 37 | var dumper = new ExpressionDumper(item, predicate.Parameters.Single()); 38 | return dumper.VisitAndFormat(predicate); 39 | } 40 | 41 | public string VisitAndFormat(Expression expression) 42 | { 43 | Visit(expression); 44 | return string.Join(", ", Members.Select(kvp => kvp.Key + " = " + kvp.Value)); 45 | } 46 | 47 | private class ReflectAccessor 48 | { 49 | public Func GetValue { get; private set; } 50 | public Action SetValue { get; private set; } 51 | 52 | public ReflectAccessor(T target, string name) 53 | { 54 | var field = typeof(T).GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 55 | if (field != null) 56 | { 57 | GetValue = () => field.GetValue(target); 58 | SetValue = value => field.SetValue(target, value); 59 | return; 60 | } 61 | 62 | var prop = typeof(T).GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 63 | if (prop != null) 64 | { 65 | GetValue = () => prop.GetValue(target, null); 66 | SetValue = value => prop.SetValue(target, value, null); 67 | return; 68 | } 69 | 70 | throw new ArgumentException(string.Format("\"{0}\" not found : Type <{1}>", name, typeof(T).Name)); 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/MasterMemory/Validation/ExpressionParameterNameModifier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace MasterMemory.Validation 5 | { 6 | public class ExpressionParameterNameModifier : ExpressionVisitor 7 | { 8 | readonly ParameterExpression modifyTarget; 9 | readonly ParameterExpression replaceExpression; 10 | 11 | public ExpressionParameterNameModifier(ParameterExpression modifyTarget, ParameterExpression replaceExpression) 12 | { 13 | this.modifyTarget = modifyTarget; 14 | this.replaceExpression = replaceExpression; 15 | } 16 | 17 | protected override Expression VisitParameter(ParameterExpression node) 18 | { 19 | if (node == modifyTarget) 20 | { 21 | return replaceExpression; 22 | } 23 | 24 | return base.VisitParameter(node); 25 | } 26 | } 27 | 28 | public static class ExpressionParameterNameModifyExtensions 29 | { 30 | public static string ToThisBodyString(this Expression> predicate) 31 | { 32 | var newNameParameter = Expression.Parameter(typeof(T), "this"); 33 | var newExpression = new ExpressionParameterNameModifier(predicate.Parameters[0], newNameParameter).Visit(predicate); 34 | return (newExpression as Expression>)!.Body.ToString(); 35 | } 36 | 37 | public static string ToSpaceBodyString(this Expression> selector) 38 | { 39 | var newNameParameter = Expression.Parameter(typeof(T), " "); 40 | var newExpression = new ExpressionParameterNameModifier(selector.Parameters[0], newNameParameter).Visit(selector); 41 | return (newExpression as Expression>)!.Body.ToString(); 42 | } 43 | 44 | public static string ToNameBodyString(this Expression> selector, string newName) 45 | { 46 | var newNameParameter = Expression.Parameter(typeof(T), newName); 47 | var newExpression = new ExpressionParameterNameModifier(selector.Parameters[0], newNameParameter).Visit(selector); 48 | return (newExpression as Expression>)!.Body.ToString(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/MasterMemory/Validation/ITableUniqueValidate.cs: -------------------------------------------------------------------------------- 1 | namespace MasterMemory.Validation 2 | { 3 | public interface ITableUniqueValidate 4 | { 5 | void ValidateUnique(ValidateResult resultSet); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/MasterMemory/Validation/ReferenceSet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.Linq.Expressions; 5 | 6 | namespace MasterMemory.Validation 7 | { 8 | public class ReferenceSet 9 | { 10 | readonly TElement item; 11 | readonly IReadOnlyList referenceTable; 12 | readonly ValidateResult resultSet; 13 | readonly string pkName; 14 | readonly Delegate pkSelector; 15 | 16 | public IReadOnlyList TableData => referenceTable; 17 | 18 | public ReferenceSet(TElement item, IReadOnlyList referenceTable, ValidateResult resultSet, string pkName, Delegate pkSelector) 19 | { 20 | this.item = item; 21 | this.referenceTable = referenceTable; 22 | this.resultSet = resultSet; 23 | this.pkName = pkName; 24 | this.pkSelector = pkSelector; 25 | } 26 | 27 | public void Exists(Expression> elementSelector, Expression> referenceElementSelector) 28 | { 29 | Exists(elementSelector, referenceElementSelector, EqualityComparer.Default); 30 | } 31 | 32 | public void Exists(Expression> elementSelector, Expression> referenceElementSelector, EqualityComparer equalityComparer) 33 | { 34 | var f1 = elementSelector.Compile(true); 35 | var f2 = referenceElementSelector.Compile(true); 36 | 37 | var compareBase = f1(item); 38 | foreach (var refItem in referenceTable) 39 | { 40 | if (equalityComparer.Equals(compareBase, f2(refItem))) 41 | { 42 | return; 43 | } 44 | } 45 | 46 | // not found, assert. 47 | var from = elementSelector.ToNameBodyString(typeof(TElement).Name); 48 | var to = referenceElementSelector.ToNameBodyString(typeof(TReference).Name); 49 | resultSet.AddFail(typeof(TElement), "Exists failed: " + from + " -> " + to + ", value = " + compareBase + ", " + BuildPkMessage(), item!); 50 | } 51 | 52 | string BuildPkMessage() 53 | { 54 | var pk = pkSelector.DynamicInvoke(item).ToString(); 55 | return $"PK({pkName}) = {pk}"; 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /src/MasterMemory/Validation/ValidatableSet.Sequential.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="true" hostSpecific="false" #> 2 | <#@ output extension=".cs" #> 3 | <#@ Assembly Name="System.Core" #> 4 | <#@ import namespace="System" #> 5 | <#@ import namespace="System.IO" #> 6 | <#@ import namespace="System.Diagnostics" #> 7 | <#@ import namespace="System.Linq" #> 8 | <#@ import namespace="System.Collections" #> 9 | <#@ import namespace="System.Collections.Generic" #> 10 | <# 11 | var targetTypes = new[] 12 | { 13 | typeof(sbyte), 14 | typeof(short), 15 | typeof(int), 16 | typeof(long), 17 | typeof(byte), 18 | typeof(ushort), 19 | typeof(uint), 20 | typeof(ulong), 21 | }; 22 | #> 23 | using System; 24 | using System.Linq; 25 | using System.Linq.Expressions; 26 | 27 | namespace MasterMemory.Validation 28 | { 29 | public partial class ValidatableSet 30 | { 31 | <# foreach(var t in targetTypes) { #> 32 | public void Sequential(Expression>> selector, bool distinct = false) 33 | { 34 | var f = selector.Compile(true); 35 | SequentialCore(f, () => selector.ToSpaceBodyString(), distinct); 36 | } 37 | 38 | public void Sequential(Func> selector, string message, bool distinct = false) 39 | { 40 | SequentialCore(selector, () => " " + message, distinct); 41 | } 42 | 43 | void SequentialCore(Func> selector, Func message, bool distinct) 44 | { 45 | if (tableData.Count == 0) return; 46 | var data = tableData.OrderBy(selector).ToArray(); 47 | 48 | var prev = selector(data[0]); 49 | for (int i = 1; i < data.Length; i++) 50 | { 51 | var curr = selector(data[i]); 52 | if (distinct) 53 | { 54 | if (prev == curr) continue; 55 | } 56 | 57 | if ((prev + 1) != curr) 58 | { 59 | resultSet.AddFail(typeof(TElement), "Sequential failed:" + message() + ", value = " + (prev, curr) + ", " + BuildPkMessage(data[i]), data[i]); 60 | } 61 | 62 | prev = curr; 63 | } 64 | } 65 | 66 | <# } #> 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/MasterMemory/Validation/ValidatableSet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | namespace MasterMemory.Validation 7 | { 8 | public partial class ValidatableSet 9 | { 10 | readonly IReadOnlyList tableData; 11 | readonly ValidateResult resultSet; 12 | readonly string pkName; 13 | readonly Delegate pkSelector; 14 | 15 | public ValidatableSet(IReadOnlyList tableData, ValidateResult resultSet, string pkName, Delegate pkSelector) 16 | { 17 | this.tableData = tableData; 18 | this.resultSet = resultSet; 19 | this.pkName = pkName; 20 | this.pkSelector = pkSelector; 21 | } 22 | 23 | public IReadOnlyList TableData => tableData; 24 | 25 | public void Unique(Expression> selector) 26 | { 27 | Unique(selector, EqualityComparer.Default); 28 | } 29 | 30 | public void Unique(Expression> selector, IEqualityComparer equalityComparer) 31 | { 32 | var f = selector.Compile(true); 33 | 34 | var set = new HashSet(equalityComparer); 35 | foreach (var item in tableData) 36 | { 37 | var v = f(item); 38 | if (!set.Add(v)) 39 | { 40 | resultSet.AddFail(typeof(TElement), "Unique failed:" + selector.ToSpaceBodyString() + ", value = " + v + ", " + BuildPkMessage(item), item!); 41 | } 42 | } 43 | } 44 | 45 | public void Unique(Func selector, string message) 46 | { 47 | Unique(selector, EqualityComparer.Default, message); 48 | } 49 | 50 | public void Unique(Func selector, IEqualityComparer equalityComparer, string message) 51 | { 52 | var set = new HashSet(equalityComparer); 53 | foreach (var item in tableData) 54 | { 55 | var v = selector(item); 56 | if (!set.Add(v)) 57 | { 58 | resultSet.AddFail(typeof(TElement), "Unique failed: " + message + ", value = " + v + ", " + BuildPkMessage(item), item!); 59 | } 60 | } 61 | } 62 | 63 | public ValidatableSet Where(Func predicate) 64 | { 65 | return new ValidatableSet(tableData.Where(predicate).ToArray(), resultSet, pkName, pkSelector); 66 | } 67 | 68 | string BuildPkMessage(TElement item) 69 | { 70 | var pk = pkSelector.DynamicInvoke(item).ToString(); 71 | return $"PK({pkName}) = {pk}"; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/MasterMemory/Validation/ValidateResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace MasterMemory.Validation 6 | { 7 | public class ValidateResult 8 | { 9 | List result = new List(); 10 | 11 | public bool IsValidationFailed => result.Count != 0; 12 | 13 | public IReadOnlyList FailedResults => result; 14 | 15 | public string FormatFailedResults() 16 | { 17 | var sb = new StringBuilder(); 18 | foreach (var item in result) 19 | { 20 | sb.AppendLine(item.Type.FullName + " - " + item.Message); 21 | } 22 | return sb.ToString(); 23 | } 24 | 25 | internal void AddFail(Type type, string message, object data) 26 | { 27 | result.Add(new FaildItem(type, message, data)); 28 | } 29 | } 30 | 31 | public readonly struct FaildItem 32 | { 33 | public FaildItem(Type type, string message, object data) 34 | { 35 | Type = type; 36 | Message = message; 37 | Data = data; 38 | } 39 | 40 | public Type Type { get; } 41 | public string Message { get; } 42 | public object Data { get; } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/MasterMemory/Validation/ValidationDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace MasterMemory.Validation 5 | { 6 | public class ValidationDatabase 7 | { 8 | // {Type, IReadOnlyList } 9 | readonly Dictionary dataTables = new Dictionary(); 10 | 11 | public ValidationDatabase(IEnumerable tables) 12 | { 13 | foreach (var table in tables) 14 | { 15 | // TableBase 16 | var baseType = table.GetType().BaseType; 17 | 18 | // RangeView 19 | var rangeViewAll = baseType.GetProperty("All").GetGetMethod().Invoke(table, null); 20 | 21 | var elementType = baseType.GetGenericArguments()[0]; 22 | dataTables.Add(elementType, rangeViewAll); 23 | } 24 | } 25 | 26 | internal IReadOnlyList GetTable() 27 | { 28 | if (!dataTables.TryGetValue(typeof(T), out var table)) 29 | { 30 | throw new InvalidOperationException("Can not create validator in " + typeof(T).FullName); 31 | } 32 | var data = table as IReadOnlyList; 33 | return data!; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/MasterMemory/Validation/Validator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace MasterMemory.Validation 6 | { 7 | internal class Validator : IValidator 8 | { 9 | readonly ValidationDatabase database; 10 | readonly T item; 11 | readonly ValidateResult resultSet; 12 | readonly StrongBox onceCalled; 13 | readonly string pkName; 14 | readonly Delegate pkSelector; 15 | 16 | public Validator(ValidationDatabase database, T item, ValidateResult resultSet, StrongBox onceCalled, string pkName, Delegate pkSelector) 17 | { 18 | this.database = database; 19 | this.item = item; 20 | this.resultSet = resultSet; 21 | this.onceCalled = onceCalled; 22 | this.pkName = pkName; 23 | this.pkSelector = pkSelector; 24 | } 25 | 26 | public bool CallOnce() 27 | { 28 | if (!onceCalled.Value) 29 | { 30 | onceCalled.Value = true; 31 | return true; 32 | } 33 | 34 | return false; 35 | } 36 | 37 | public ValidatableSet GetTableSet() 38 | { 39 | return new ValidatableSet(database.GetTable(), resultSet, pkName, pkSelector); 40 | } 41 | 42 | public ReferenceSet GetReferenceSet() 43 | { 44 | var table = database.GetTable(); 45 | return new ReferenceSet(item, table, resultSet, pkName, pkSelector); 46 | } 47 | 48 | public void Validate(Expression> predicate) 49 | { 50 | if (!predicate.Compile(true).Invoke(item)) 51 | { 52 | var memberValues = ExpressionDumper.DumpMemberValues(item, predicate); 53 | var message = string.Format($"{predicate.ToThisBodyString()}, {memberValues}, {BuildPkMessage()}"); 54 | resultSet.AddFail(typeof(T), "Validate failed: " + message, item!); 55 | } 56 | } 57 | 58 | public void Validate(Func predicate, string message) 59 | { 60 | if (!predicate(item)) 61 | { 62 | resultSet.AddFail(typeof(T), "Validate failed: " + message + ", " + BuildPkMessage(), item!); 63 | } 64 | } 65 | 66 | public void ValidateAction(Expression> predicate) 67 | { 68 | if (!predicate.Compile(true).Invoke()) 69 | { 70 | var expr = predicate.Body.ToString(); 71 | resultSet.AddFail(typeof(T), "ValidateAction failed: " + expr + ", " + BuildPkMessage(), item!); 72 | } 73 | } 74 | 75 | public void ValidateAction(Func predicate, string message) 76 | { 77 | if (!predicate()) 78 | { 79 | resultSet.AddFail(typeof(T), "ValidateAction failed: " + message + ", " + BuildPkMessage(), item!); 80 | } 81 | } 82 | 83 | public void Fail(string message) 84 | { 85 | resultSet.AddFail(typeof(T), message + ", " + BuildPkMessage(), item!); 86 | } 87 | 88 | string BuildPkMessage() 89 | { 90 | var pk = pkSelector.DynamicInvoke(item).ToString(); 91 | return $"PK({pkName}) = {pk}"; 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /src/MasterMemory/_InternalVisibleTo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("MasterMemory.Tests")] -------------------------------------------------------------------------------- /src/MasterMemory/_MessagePackResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace MasterMemory; 6 | 7 | [MessagePack.GeneratedMessagePackResolver] 8 | internal partial class _MessagePackResolver 9 | { 10 | } -------------------------------------------------------------------------------- /src/MasterMemory/bin/Debug/netstandard2.0/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.github.mastermemory.internal", 3 | "displayName": "MasterMemory Internal", 4 | "author": { "name": "MasterMemory", "url": "https://github.com/Cysharp/MasterMemory" }, 5 | "version": "1.0.0", 6 | "unity": "2022.3", 7 | "description": "Internal Package of MasterMemory for development time.", 8 | "keywords": [ "Database" ], 9 | "license": "MIT", 10 | "category": "Scripting", 11 | "dependencies": {} 12 | } 13 | -------------------------------------------------------------------------------- /src/MasterMemory/bin/Debug/netstandard2.0/package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b73e5f61035018248a0ae4ff5130ae2a 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /tests/MasterMemory.SourceGenerator.Tests/AssemblyAtrributeTest.cs: -------------------------------------------------------------------------------- 1 | namespace MasterMemory.SourceGenerator.Tests; 2 | 3 | public class AssemblyAtrributeTest(ITestOutputHelper outputHelper) : TestBase(outputHelper) 4 | { 5 | [Fact] 6 | public void NoGeneratorOptions() 7 | { 8 | var codes = Helper.GenerateCode(""" 9 | [MemoryTable("item")] 10 | public class Item 11 | { 12 | [PrimaryKey] 13 | public int ItemId { get; set; } 14 | } 15 | """); 16 | 17 | codes.TryGetValue("MasterMemory.DatabaseBuilder.g.cs", out _).Should().BeTrue(); 18 | 19 | var mainCode = codes["MasterMemory.ItemTable.g.cs"]; 20 | WriteLine(mainCode); 21 | 22 | mainCode.Should().Contain("namespace MasterMemory.Tables"); 23 | mainCode.Should().Contain("return ThrowKeyNotFound(key);"); 24 | mainCode.Should().Contain("public sealed partial class ItemTable"); 25 | } 26 | 27 | 28 | [Fact] 29 | public void FullOptions() 30 | { 31 | var codes = Helper.GenerateCode(""" 32 | [assembly: MasterMemoryGeneratorOptions( 33 | Namespace = "MyNamespace", 34 | IsReturnNullIfKeyNotFound = true, 35 | PrefixClassName = "FooBarBaz")] 36 | 37 | [MemoryTable("item")] 38 | public class Item 39 | { 40 | [PrimaryKey] 41 | public int ItemId { get; set; } 42 | } 43 | """); 44 | 45 | codes.TryGetValue("MasterMemory.FooBarBazDatabaseBuilder.g.cs", out _).Should().BeTrue(); 46 | 47 | var mainCode = codes["MasterMemory.ItemTable.g.cs"]; 48 | WriteLine(mainCode); 49 | 50 | mainCode.Should().Contain("namespace MyNamespace.Tables"); 51 | mainCode.Should().NotContain("return ThrowKeyNotFound(key);"); 52 | mainCode.Should().Contain("public sealed partial class ItemTable"); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/MasterMemory.SourceGenerator.Tests/DiagnosticsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MasterMemory.SourceGenerator.Tests; 8 | 9 | public class DiagnosticsTest(ITestOutputHelper outputHelper) : TestBase(outputHelper) 10 | { 11 | [Fact] 12 | public void RequirePrimaryKey() 13 | { 14 | Helper.Verify(1, """ 15 | [MemoryTable("item")] 16 | public class Item 17 | { 18 | // [PrimaryKey] // No PrimaryKey 19 | public int ItemId { get; set; } 20 | } 21 | """, "Item"); 22 | } 23 | 24 | [Fact] 25 | public void DuplicateSecondaryKey() 26 | { 27 | Helper.Verify(3, """ 28 | [MemoryTable("item")] 29 | public class Item 30 | { 31 | [PrimaryKey] 32 | public int ItemId1 { get; set; } 33 | [SecondaryKey(0), SecondaryKey(1)] 34 | public int ItemId2 { get; set; } 35 | } 36 | """, "ItemId2"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/MasterMemory.SourceGenerator.Tests/GenerateTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xunit.Abstractions; 7 | 8 | namespace MasterMemory.SourceGenerator.Tests; 9 | 10 | public class GenerateTest(ITestOutputHelper outputHelper) : TestBase(outputHelper) 11 | { 12 | [Fact] 13 | public void GenerateClass() 14 | { 15 | Helper.Ok(""" 16 | [MemoryTable("item")] 17 | public class Item 18 | { 19 | [PrimaryKey] 20 | public int ItemId { get; set; } 21 | } 22 | """); 23 | } 24 | 25 | [Fact] 26 | public void GenerateRecord() 27 | { 28 | Helper.Ok(""" 29 | [MemoryTable("item")] 30 | public record Item 31 | { 32 | [PrimaryKey] 33 | public int ItemId { get; set; } 34 | } 35 | """); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/MasterMemory.SourceGenerator.Tests/IncrementalGeneratorTest.cs: -------------------------------------------------------------------------------- 1 | namespace MasterMemory.SourceGenerator.Tests; 2 | 3 | public class IncrementalGeneratorTest 4 | { 5 | void VerifySourceOutputReasonIsCached((string Key, string Reasons)[] reasons) 6 | { 7 | var reason = reasons.FirstOrDefault(x => x.Key == "SourceOutput").Reasons; 8 | reason.Should().Be("Cached"); 9 | } 10 | 11 | void VerifySourceOutputReasonIsNotCached((string Key, string Reasons)[] reasons) 12 | { 13 | var reason = reasons.FirstOrDefault(x => x.Key == "SourceOutput").Reasons; 14 | reason.Should().NotBe("Cached"); 15 | } 16 | 17 | [Fact] 18 | public void CheckReasons() 19 | { 20 | var step1 = """ 21 | [MemoryTable("item")] 22 | public class Item 23 | { 24 | [PrimaryKey] 25 | public int ItemId { get; set; } 26 | } 27 | """; 28 | 29 | var step2 = """ 30 | [MemoryTable("item")] 31 | public class Item 32 | { 33 | // add unrelated line 34 | [PrimaryKey] 35 | public int ItemId { get; set; } 36 | } 37 | """; 38 | 39 | var step3 = """ 40 | [MemoryTable("item")] 41 | public class Item 42 | { 43 | [PrimaryKey] 44 | public int ItemId2 { get; set; } // changed name 45 | } 46 | """; 47 | 48 | var reasons = CSharpGeneratorRunner.GetIncrementalGeneratorTrackedStepsReasons("MasterMemory.SyntaxProvider.", step1, step2, step3); 49 | 50 | VerifySourceOutputReasonIsCached(reasons[1]); 51 | VerifySourceOutputReasonIsNotCached(reasons[2]); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/MasterMemory.SourceGenerator.Tests/MasterMemory.SourceGenerator.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /tests/MasterMemory.SourceGenerator.Tests/TestBase.cs: -------------------------------------------------------------------------------- 1 | namespace MasterMemory.SourceGenerator.Tests; 2 | 3 | public abstract class TestBase(ITestOutputHelper testoutputHelper) 4 | { 5 | protected CodeGeneratorHelper Helper = new CodeGeneratorHelper(testoutputHelper, "MAM"); 6 | 7 | protected void WriteLine(string message) 8 | { 9 | testoutputHelper.WriteLine(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/MasterMemory.SourceGenerator.Tests/Utility/CodeGeneratorHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Runtime.CompilerServices; 4 | 5 | public class CodeGeneratorHelper(ITestOutputHelper output, string idPrefix) 6 | { 7 | // Diagnostics Verify 8 | 9 | public void Ok([StringSyntax("C#-test")] string code, [CallerArgumentExpression("code")] string? codeExpr = null) 10 | { 11 | output.WriteLine(codeExpr); 12 | 13 | var (compilation, diagnostics) = CSharpGeneratorRunner.RunGenerator(code); 14 | foreach (var item in diagnostics) 15 | { 16 | output.WriteLine(item.ToString()); 17 | } 18 | OutputGeneratedCode(compilation); 19 | 20 | diagnostics.Length.Should().Be(0); 21 | } 22 | 23 | public Dictionary GenerateCode([StringSyntax("C#-test")] string code, [CallerArgumentExpression("code")] string? codeExpr = null) 24 | { 25 | var (compilation, diagnostics) = CSharpGeneratorRunner.RunGenerator(code); 26 | foreach (var item in diagnostics) 27 | { 28 | output.WriteLine(item.ToString()); 29 | } 30 | diagnostics.Length.Should().Be(0); 31 | 32 | var dict = new Dictionary(); 33 | foreach (var syntaxTree in compilation.SyntaxTrees) 34 | { 35 | // only shows ConsoleApp.Run/Builder generated code 36 | if (!syntaxTree.FilePath.Contains("g.cs")) continue; 37 | var generatedCode = syntaxTree.ToString(); 38 | var fileName = Path.GetFileName(syntaxTree.FilePath); 39 | dict.Add(fileName, generatedCode); 40 | } 41 | 42 | return dict; 43 | } 44 | 45 | public void Verify(int id, [StringSyntax("C#-test")] string code, string diagnosticsCodeSpan, [CallerArgumentExpression("code")] string? codeExpr = null) 46 | { 47 | output.WriteLine(codeExpr); 48 | 49 | var (compilation, diagnostics) = CSharpGeneratorRunner.RunGenerator(code); 50 | foreach (var item in diagnostics) 51 | { 52 | output.WriteLine(item.ToString()); 53 | } 54 | OutputGeneratedCode(compilation); 55 | 56 | diagnostics.Length.Should().Be(1); 57 | diagnostics[0].Id.Should().Be(idPrefix + id.ToString("000")); 58 | 59 | var text = GetLocationText(diagnostics[0], compilation.SyntaxTrees); 60 | text.Should().Be(diagnosticsCodeSpan); 61 | } 62 | 63 | public (string, string)[] Verify([StringSyntax("C#-test")] string code, [CallerArgumentExpression("code")] string? codeExpr = null) 64 | { 65 | output.WriteLine(codeExpr); 66 | 67 | var (compilation, diagnostics) = CSharpGeneratorRunner.RunGenerator(code); 68 | OutputGeneratedCode(compilation); 69 | return diagnostics.Select(x => (x.Id, GetLocationText(x, compilation.SyntaxTrees))).ToArray(); 70 | } 71 | 72 | // Execute and check stdout result 73 | 74 | public void Execute([StringSyntax("C#-test")] string code, string args, string expected, [CallerArgumentExpression("code")] string? codeExpr = null) 75 | { 76 | output.WriteLine(codeExpr); 77 | 78 | var (compilation, diagnostics, stdout) = CSharpGeneratorRunner.CompileAndExecute(code, args == "" ? [] : args.Split(' ')); 79 | foreach (var item in diagnostics) 80 | { 81 | output.WriteLine(item.ToString()); 82 | } 83 | OutputGeneratedCode(compilation); 84 | 85 | stdout.Should().Be(expected); 86 | } 87 | 88 | public string Error([StringSyntax("C#-test")] string code, string args, [CallerArgumentExpression("code")] string? codeExpr = null) 89 | { 90 | output.WriteLine(codeExpr); 91 | 92 | var (compilation, diagnostics, stdout) = CSharpGeneratorRunner.CompileAndExecute(code, args == "" ? [] : args.Split(' ')); 93 | foreach (var item in diagnostics) 94 | { 95 | output.WriteLine(item.ToString()); 96 | } 97 | OutputGeneratedCode(compilation); 98 | 99 | return stdout; 100 | } 101 | 102 | string GetLocationText(Diagnostic diagnostic, IEnumerable syntaxTrees) 103 | { 104 | var location = diagnostic.Location; 105 | 106 | var textSpan = location.SourceSpan; 107 | var sourceTree = location.SourceTree; 108 | if (sourceTree == null) 109 | { 110 | var lineSpan = location.GetLineSpan(); 111 | if (lineSpan.Path == null) return ""; 112 | 113 | sourceTree = syntaxTrees.FirstOrDefault(x => x.FilePath == lineSpan.Path); 114 | if (sourceTree == null) return ""; 115 | } 116 | 117 | var text = sourceTree.GetText().GetSubText(textSpan).ToString(); 118 | return text; 119 | } 120 | 121 | void OutputGeneratedCode(Compilation compilation) 122 | { 123 | foreach (var syntaxTree in compilation.SyntaxTrees) 124 | { 125 | // only shows ConsoleApp.Run/Builder generated code 126 | if (!syntaxTree.FilePath.Contains("g.cs")) continue; 127 | output.WriteLine(syntaxTree.ToString()); 128 | } 129 | } 130 | } -------------------------------------------------------------------------------- /tests/MasterMemory.Tests/BinarySearchTest.cs: -------------------------------------------------------------------------------- 1 | using MasterMemory.Internal; 2 | using MessagePack.Resolvers; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Xunit; 10 | using MessagePack; 11 | 12 | namespace MasterMemory.Tests 13 | { 14 | public class BinarySearchTest 15 | { 16 | public BinarySearchTest() 17 | { 18 | MessagePackSerializer.DefaultOptions = MessagePackSerializer.DefaultOptions.WithResolver(MessagePackResolver.Instance); 19 | } 20 | 21 | [Fact] 22 | public void Find() 23 | { 24 | var rand = new Random(); 25 | for (int iii = 0; iii < 30; iii++) 26 | { 27 | var seed = Enumerable.Range(1, 10); 28 | var randomSeed = seed.Where(x => rand.Next() % 2 == 0); 29 | 30 | var array = randomSeed.Concat(randomSeed).Concat(randomSeed).Concat(randomSeed) 31 | .OrderBy(x => x) 32 | .ToArray(); 33 | 34 | for (int i = 1; i <= 10; i++) 35 | { 36 | var firstIndex = Array.IndexOf(array, i); 37 | var lastIndex = Array.LastIndexOf(array, i); 38 | 39 | var f = BinarySearch.FindFirst(array, i, x => x, Comparer.Default); 40 | var l = BinarySearch.LowerBound(array, 0, array.Length, i, x => x, Comparer.Default); 41 | var u = BinarySearch.UpperBound(array, 0, array.Length, i, x => x, Comparer.Default); 42 | 43 | // not found 44 | if (firstIndex == -1) 45 | { 46 | f.Should().Be(-1); 47 | l.Should().Be(-1); 48 | u.Should().Be(-1); 49 | continue; 50 | } 51 | 52 | array[f].Should().Be(i); 53 | array[l].Should().Be(i); 54 | array[u].Should().Be(i); 55 | 56 | l.Should().Be(firstIndex); 57 | u.Should().Be(lastIndex); 58 | } 59 | } 60 | 61 | // and empty 62 | var emptyArray = Enumerable.Empty().ToArray(); 63 | BinarySearch.FindFirst(emptyArray, 0, x => x, Comparer.Default).Should().Be(-1); 64 | BinarySearch.LowerBound(emptyArray, 0, emptyArray.Length, 0, x => x, Comparer.Default).Should().Be(-1); 65 | BinarySearch.UpperBound(emptyArray, 0, emptyArray.Length, 0, x => x, Comparer.Default).Should().Be(-1); 66 | } 67 | 68 | [Fact] 69 | public void Closest() 70 | { 71 | // empty 72 | var array = Enumerable.Empty().ToArray(); 73 | 74 | var near = BinarySearch.FindClosest(array, 0, 0, array.Length, x => x, Comparer.Default, false); 75 | near.Should().Be(-1); 76 | 77 | // mid 78 | var source = new[]{ 79 | new { id = 0, bound = 0 }, 80 | new { id = 1, bound = 100 }, 81 | new { id = 2, bound = 200 }, 82 | new { id = 3, bound = 300 }, 83 | new { id = 4, bound = 500 }, 84 | new { id = 5, bound = 1000 }, 85 | }; 86 | 87 | BinarySearch.FindClosest(source, 0, source.Length, -100, x => x.bound, Comparer.Default, true).Should().Be(-1); 88 | // BinarySearch.FindClosest(source, 0, source.Length, -100, x => x.bound, Comparer.Default, true).Should().Be(0); 89 | BinarySearch.FindClosest(source, 0, source.Length, 0, x => x.bound, Comparer.Default, true).Should().Be(0); 90 | BinarySearch.FindClosest(source, 0, source.Length, 10, x => x.bound, Comparer.Default, true).Should().Be(0); 91 | BinarySearch.FindClosest(source, 0, source.Length, 50, x => x.bound, Comparer.Default, true).Should().Be(0); 92 | 93 | source[BinarySearch.FindClosest(source, 0, source.Length, 100, x => x.bound, Comparer.Default, true)].id.Should().Be(1); 94 | source[BinarySearch.FindClosest(source, 0, source.Length, 100, x => x.bound, Comparer.Default, false)].id.Should().Be(1); 95 | 96 | source[BinarySearch.FindClosest(source, 0, source.Length, 150, x => x.bound, Comparer.Default, true)].id.Should().Be(1); 97 | source[BinarySearch.FindClosest(source, 0, source.Length, 300, x => x.bound, Comparer.Default, true)].id.Should().Be(3); 98 | source[BinarySearch.FindClosest(source, 0, source.Length, 999, x => x.bound, Comparer.Default, true)].id.Should().Be(4); 99 | source[BinarySearch.FindClosest(source, 0, source.Length, 1000, x => x.bound, Comparer.Default, true)].id.Should().Be(5); 100 | source[BinarySearch.FindClosest(source, 0, source.Length, 1001, x => x.bound, Comparer.Default, true)].id.Should().Be(5); 101 | source[BinarySearch.FindClosest(source, 0, source.Length, 10000, x => x.bound, Comparer.Default, true)].id.Should().Be(5); 102 | // source[BinarySearch.FindClosest(source, 0, source.Length, 10000, x => x.bound, Comparer.Default, false)].id.Should().Be(5); 103 | 104 | BinarySearch.FindClosest(source, 0, source.Length, 10000, x => x.bound, Comparer.Default, false).Should().Be(6); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /tests/MasterMemory.Tests/DatabaseTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using MessagePack; 3 | using MessagePack.Resolvers; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using Xunit; 10 | 11 | namespace MasterMemory.Tests 12 | { 13 | public class DatabaseTest 14 | { 15 | public DatabaseTest() 16 | { 17 | MessagePackSerializer.DefaultOptions = MessagePackSerializer.DefaultOptions.WithResolver(MessagePackResolver.Instance); 18 | } 19 | 20 | Sample[] CreateData() 21 | { 22 | // Id = Unique, PK 23 | // FirstName + LastName = Unique 24 | var data = new[] 25 | { 26 | new Sample { Id = 5, Age = 19, FirstName = "aaa", LastName = "foo" }, 27 | new Sample { Id = 6, Age = 29, FirstName = "bbb", LastName = "foo" }, 28 | new Sample { Id = 7, Age = 39, FirstName = "ccc", LastName = "foo" }, 29 | new Sample { Id = 8, Age = 49, FirstName = "ddd", LastName = "foo" }, 30 | new Sample { Id = 1, Age = 59, FirstName = "eee", LastName = "foo" }, 31 | new Sample { Id = 2, Age = 89, FirstName = "aaa", LastName = "bar" }, 32 | new Sample { Id = 3, Age = 79, FirstName = "be", LastName = "de" }, 33 | new Sample { Id = 4, Age = 89, FirstName = "aaa", LastName = "tako" }, 34 | new Sample { Id = 9, Age = 99, FirstName = "aaa", LastName = "ika" }, 35 | new Sample { Id = 10, Age = 9, FirstName = "eee", LastName = "baz" }, 36 | }; 37 | return data; 38 | } 39 | 40 | [Fact] 41 | public void SingleDb() 42 | { 43 | var builder = new DatabaseBuilder(); 44 | builder.Append(CreateData()); 45 | 46 | var bin = builder.Build(); 47 | var db = new MemoryDatabase(bin); 48 | db.SampleTable.FindById(8).Age.Should().Be(49); 49 | 50 | var tableInfo = MemoryDatabase.GetTableInfo(bin); 51 | tableInfo[0].TableName.Should().Be("s_a_m_p_l_e"); 52 | } 53 | 54 | [Fact] 55 | public void All() 56 | { 57 | var builder = new DatabaseBuilder(); 58 | builder.Append(CreateData()); 59 | 60 | var bin = builder.Build(); 61 | var db = new MemoryDatabase(bin); 62 | 63 | db.SampleTable.All.Select(x => x.Id).ToArray().Should().BeEquivalentTo(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); 64 | db.SampleTable.AllReverse.Select(x => x.Id).ToArray().Should().BeEquivalentTo(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }.Reverse()); 65 | db.SampleTable.SortByAge.Select(x => x.Id).OrderBy(x => x).ToArray().Should().BeEquivalentTo(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); 66 | } 67 | 68 | [Fact] 69 | public void Ranges() 70 | { 71 | var builder = new DatabaseBuilder(); 72 | builder.Append(CreateData()); 73 | 74 | var bin = builder.Build(); 75 | var db = new MemoryDatabase(bin); 76 | 77 | db.SampleTable.FindRangeByAge(2,2).Select(x=>x.Id).ToArray().Should().BeEquivalentTo( new int[] {} ); 78 | db.SampleTable.FindRangeByAge(30,50).Select(x=>x.Id).ToArray().Should().BeEquivalentTo( new int[] { 7, 8 } ); 79 | db.SampleTable.FindRangeByAge(100,100).Select(x=>x.Id).ToArray().Should().BeEquivalentTo( new int[] {} ); 80 | } 81 | 82 | 83 | [Fact] 84 | public void EmptyAll() 85 | { 86 | { 87 | var builder = new DatabaseBuilder(); 88 | builder.Append(new Sample[] { }); 89 | 90 | var bin = builder.Build(); 91 | var db = new MemoryDatabase(bin); 92 | 93 | db.SampleTable.All.Any().Should().BeFalse(); 94 | } 95 | { 96 | var builder = new DatabaseBuilder(); 97 | builder.Append(new Sample[] { }.Select(x => x)); 98 | 99 | var bin = builder.Build(); 100 | var db = new MemoryDatabase(bin); 101 | 102 | db.SampleTable.All.Any().Should().BeFalse(); 103 | } 104 | } 105 | 106 | [Fact] 107 | public void WithNull() 108 | { 109 | var builder = new DatabaseBuilder(); 110 | builder.Append(new Sample[] {new Sample 111 | { 112 | Age = 10, 113 | FirstName = null, 114 | Id = 999, 115 | LastName = "abcde" 116 | } }); 117 | 118 | var bin = builder.Build(); 119 | var db = new MemoryDatabase(bin); 120 | 121 | var sample = db.SampleTable.FindById(999); 122 | sample.Age.Should().Be(10); 123 | sample.FirstName.Should().BeNull(); 124 | sample.LastName.Should().Be("abcde"); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /tests/MasterMemory.Tests/IssueTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using MasterMemory.Tests.TestStructures; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using Xunit; 8 | 9 | namespace MasterMemory.Tests 10 | { 11 | public class IssueTest 12 | { 13 | //[Fact] 14 | //public void Issue49() 15 | //{ 16 | // var builder = new DatabaseBuilder().Append(new[] 17 | // { 18 | // new PersonModel { FirstName = "realname", LastName="reallast" }, 19 | // new PersonModel { FirstName = "fakefirst", LastName="fakelast" }, 20 | // }); 21 | 22 | // var data = builder.Build(); 23 | // var database = new MemoryDatabase(data); 24 | 25 | // var entries = database.PersonModelTable.FindClosestByFirstNameAndLastName(("real", "real"), false); 26 | // var firstEntry = entries.FirstOrDefault(); 27 | 28 | // var firstIs = firstEntry.FirstName; 29 | 30 | //} 31 | 32 | Sample[] CreateData() 33 | { 34 | // Id = Unique, PK 35 | // FirstName + LastName = Unique 36 | var data = new[] 37 | { 38 | new Sample { Id = 5, Age = 19, FirstName = "aaa", LastName = "foo" }, 39 | new Sample { Id = 6, Age = 29, FirstName = "bbb", LastName = "foo" }, 40 | new Sample { Id = 7, Age = 39, FirstName = "ccc", LastName = "foo" }, 41 | new Sample { Id = 8, Age = 49, FirstName = "ddd", LastName = "foo" }, 42 | new Sample { Id = 1, Age = 59, FirstName = "eee", LastName = "foo" }, 43 | new Sample { Id = 2, Age = 89, FirstName = "aaa", LastName = "bar" }, 44 | new Sample { Id = 3, Age = 79, FirstName = "be", LastName = "de" }, 45 | new Sample { Id = 4, Age = 89, FirstName = "aaa", LastName = "tako" }, 46 | new Sample { Id = 9, Age = 99, FirstName = "aaa", LastName = "ika" }, 47 | new Sample { Id = 10, Age = 9, FirstName = "eee", LastName = "baz" }, 48 | }; 49 | return data; 50 | } 51 | 52 | [Fact] 53 | public void Issue57() 54 | { 55 | var builder = new DatabaseBuilder(); 56 | builder.Append(CreateData()); 57 | 58 | var bin = builder.Build(); 59 | var db = new MemoryDatabase(bin); 60 | 61 | db.SampleTable.FindRangeByAge(2, 2).Select(x => x.Id).ToArray().Should().BeEquivalentTo(new int[] { }); 62 | db.SampleTable.FindRangeByAge(30, 50).Select(x => x.Id).ToArray().Should().BeEquivalentTo(new int[] { 7, 8 }); 63 | db.SampleTable.FindRangeByAge(100, 100).Select(x => x.Id).ToArray().Should().BeEquivalentTo(new int[] { }); 64 | } 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/MasterMemory.Tests/MasterMemory.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Analyzer 19 | false 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/MasterMemory.Tests/MemoryKeyTest.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using System.Linq; 3 | using MasterMemory.Tests.Tables; 4 | using FluentAssertions; 5 | using MessagePack; 6 | using System.Collections.Generic; 7 | 8 | namespace MasterMemory.Tests 9 | { 10 | public class MemoryKeyMemoryTest 11 | { 12 | public MemoryKeyMemoryTest() 13 | { 14 | MessagePackSerializer.DefaultOptions = MessagePackSerializer.DefaultOptions.WithResolver(MessagePackResolver.Instance); 15 | } 16 | 17 | Sample[] CreateData() 18 | { 19 | // Id = Unique, PK 20 | // FirstName + LastName = Unique 21 | var data = new[] 22 | { 23 | new Sample { Id = 5, Age = 19, FirstName = "aaa", LastName = "foo" }, 24 | new Sample { Id = 6, Age = 29, FirstName = "bbb", LastName = "foo" }, 25 | new Sample { Id = 7, Age = 39, FirstName = "ccc", LastName = "foo" }, 26 | new Sample { Id = 8, Age = 49, FirstName = "ddd", LastName = "foo" }, 27 | new Sample { Id = 1, Age = 59, FirstName = "eee", LastName = "foo" }, 28 | new Sample { Id = 2, Age = 89, FirstName = "aaa", LastName = "bar" }, 29 | new Sample { Id = 3, Age = 79, FirstName = "be", LastName = "de" }, 30 | new Sample { Id = 4, Age = 89, FirstName = "aaa", LastName = "tako" }, 31 | new Sample { Id = 9, Age = 99, FirstName = "aaa", LastName = "ika" }, 32 | new Sample { Id = 10, Age = 9, FirstName = "eee", LastName = "baz" }, 33 | }; 34 | return data; 35 | } 36 | 37 | SampleTable CreateTable() 38 | { 39 | return new MemoryDatabase(new DatabaseBuilder().Append(CreateData()).Build()).SampleTable; 40 | } 41 | 42 | [Fact] 43 | public void Unique() 44 | { 45 | var table = CreateTable(); 46 | 47 | table.FindById(8).Id.Should().Be(8); 48 | Assert.Throws(() => table.FindById(100)); 49 | 50 | table.FindByIdAndAge((4, 89)).Id.Should().Be(4); 51 | 52 | Assert.Throws(() => table.FindByIdAndAge((4, 899))); 53 | Assert.Throws(() => table.FindByIdAndAge((5, 89))); 54 | 55 | table.FindByIdAndAgeAndFirstName((6, 29, "bbb")).Id.Should().Be(6); 56 | Assert.Throws(() => table.FindByIdAndAgeAndFirstName((6, 29, "bbbz"))); 57 | } 58 | 59 | [Fact] 60 | public void Range() 61 | { 62 | var table = CreateTable(); 63 | 64 | var test = table.FindByFirstName("eee"); 65 | 66 | table.FindByFirstName("eee").Select(x => x.Id).OrderBy(x => x).Should().BeEquivalentTo(new[] { 1, 10 }); 67 | table.FindByFirstName("eeee").Count.Should().Be(0); 68 | 69 | table.FindClosestByFirstNameAndLastName(("aaa", "takz")).Id.Should().Be(4); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/MasterMemory.Tests/MessagePackResolver.cs: -------------------------------------------------------------------------------- 1 | using MessagePack; 2 | using MessagePack.Resolvers; 3 | 4 | namespace MasterMemory.Tests; 5 | 6 | [CompositeResolver(typeof(MasterMemoryResolver), typeof(StandardResolver))] 7 | public partial class MessagePackResolver; -------------------------------------------------------------------------------- /tests/MasterMemory.Tests/MetaTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Xunit; 6 | 7 | namespace MasterMemory.Tests 8 | { 9 | public class MetaTest 10 | { 11 | [Fact] 12 | public void Meta() 13 | { 14 | var metaDb = MemoryDatabase.GetMetaDatabase(); 15 | 16 | var sampleTable = metaDb.GetTableInfo("s_a_m_p_l_e"); 17 | 18 | sampleTable.TableName.Should().Be("s_a_m_p_l_e"); 19 | 20 | sampleTable.Properties[0].Name.Should().Be("Id"); 21 | sampleTable.Properties[0].NameLowerCamel.Should().Be("id"); 22 | sampleTable.Properties[0].NameSnakeCase.Should().Be("id"); 23 | 24 | sampleTable.Properties[2].Name.Should().Be("FirstName"); 25 | sampleTable.Properties[2].NameLowerCamel.Should().Be("firstName"); 26 | sampleTable.Properties[2].NameSnakeCase.Should().Be("first_name"); 27 | 28 | var primary = sampleTable.Indexes[0]; 29 | primary.IsUnique.Should().BeTrue(); 30 | primary.IndexProperties[0].Name.Should().Be("Id"); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/MasterMemory.Tests/RangeViewTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using Xunit; 7 | 8 | namespace MasterMemory.Tests 9 | { 10 | public class RangeViewTest 11 | { 12 | [Fact] 13 | public void Range() 14 | { 15 | // 4 -> 8 16 | { 17 | var range = new RangeView(new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 4, 8, true); 18 | 19 | range.Count.Should().Be(5); 20 | range[0].Should().Be(4); 21 | range[1].Should().Be(5); 22 | range[2].Should().Be(6); 23 | range[3].Should().Be(7); 24 | range[4].Should().Be(8); 25 | 26 | Assert.Throws(() => range[-1]); 27 | Assert.Throws(() => range[5]); 28 | 29 | var begin = 4; 30 | foreach (var item in range) 31 | { 32 | item.Should().Be(begin++); 33 | } 34 | 35 | var xs = new int[10]; 36 | range.CopyTo(xs, 3); 37 | xs[3].Should().Be(4); 38 | xs[4].Should().Be(5); 39 | xs[5].Should().Be(6); 40 | xs[6].Should().Be(7); 41 | xs[7].Should().Be(8); 42 | xs[8].Should().Be(0); 43 | 44 | range.IndexOf(5).Should().Be(1); 45 | range.IndexOf(9).Should().Be(-1); 46 | 47 | 48 | range.Contains(5).Should().BeTrue(); 49 | range.Contains(9).Should().BeFalse(); 50 | } 51 | { 52 | // 7 -> 2 53 | var range = new RangeView(new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7, false); 54 | 55 | range.Count.Should().Be(6); 56 | range[0].Should().Be(7); 57 | range[1].Should().Be(6); 58 | range[2].Should().Be(5); 59 | range[3].Should().Be(4); 60 | range[4].Should().Be(3); 61 | range[5].Should().Be(2); 62 | 63 | Assert.Throws(() => range[-1]); 64 | Assert.Throws(() => range[6]); 65 | 66 | var begin = 7; 67 | foreach (var item in range) 68 | { 69 | item.Should().Be(begin--); 70 | } 71 | 72 | var xs = new int[10]; 73 | range.CopyTo(xs, 3); 74 | xs[3].Should().Be(7); 75 | xs[4].Should().Be(6); 76 | xs[5].Should().Be(5); 77 | xs[6].Should().Be(4); 78 | xs[7].Should().Be(3); 79 | xs[8].Should().Be(2); 80 | 81 | range.IndexOf(5).Should().Be(2); 82 | range.IndexOf(9).Should().Be(-1); 83 | 84 | range.Contains(5).Should().BeTrue(); 85 | range.Contains(9).Should().BeFalse(); 86 | } 87 | 88 | var empty = new RangeView(Enumerable.Empty().ToArray(), 0, 0, true); 89 | empty.Count.Should().Be(0); 90 | 91 | var same = new RangeView(Enumerable.Range(1, 1000).ToArray(), 100, 100, true); 92 | same.Count.Should().Be(1); 93 | same[0].Should().Be(101); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /tests/MasterMemory.Tests/TestStructures/PersonModel.cs: -------------------------------------------------------------------------------- 1 | using MessagePack; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace MasterMemory.Tests.TestStructures 7 | { 8 | 9 | [MemoryTable("people"), MessagePackObject(true)] 10 | public class PersonModel 11 | { 12 | [SecondaryKey(0), NonUnique] 13 | [SecondaryKey(1, keyOrder: 1), NonUnique] 14 | public string LastName { get; set; } 15 | 16 | [SecondaryKey(2), NonUnique] 17 | [SecondaryKey(1, keyOrder: 0), NonUnique] 18 | public string FirstName { get; set; } 19 | 20 | [PrimaryKey] public string RandomId { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/MasterMemory.Tests/TestStructures/QuestMaster.cs: -------------------------------------------------------------------------------- 1 | using MessagePack; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace MasterMemory.Tests.TestStructures 7 | { 8 | [MemoryTable("quest_master"), MessagePackObject(true)] 9 | public class QuestMaster : IValidatable 10 | { 11 | [PrimaryKey] 12 | public int QuestId { get; set; } 13 | public string Name { get; set; } 14 | public int RewardItemId { get; set; } 15 | public int Cost { get; set; } 16 | 17 | public void Validate(IValidator validator) 18 | { 19 | var itemMaster = validator.GetReferenceSet(); 20 | 21 | itemMaster.Exists(x => x.RewardItemId, x => x.ItemId); 22 | 23 | validator.Validate(x => x.Cost <= 100); 24 | validator.Validate(x => x.Cost >= 0, ">= 0!!!"); 25 | 26 | validator.ValidateAction(() => this.Cost <= 1000); 27 | validator.ValidateAction(() => this.Cost >= -90, ">= -90!!!"); 28 | 29 | if (validator.CallOnce()) 30 | { 31 | var quests = validator.GetTableSet(); 32 | quests.Unique(x => x.Name); 33 | } 34 | } 35 | } 36 | 37 | [MemoryTable("item_master"), MessagePackObject(true)] 38 | public class ItemMaster : IValidatable 39 | { 40 | [PrimaryKey] 41 | public int ItemId { get; set; } 42 | 43 | public void Validate(IValidator validator) 44 | { 45 | } 46 | } 47 | 48 | [MemoryTable("quest_master_empty"), MessagePackObject(true)] 49 | public class QuestMasterEmptyValidate 50 | { 51 | [PrimaryKey] 52 | public int QuestId { get; set; } 53 | public string Name { get; set; } 54 | public int RewardItemId { get; set; } 55 | public int Cost { get; set; } 56 | } 57 | 58 | [MemoryTable("item_master_empty"), MessagePackObject(true)] 59 | public class ItemMasterEmptyValidate 60 | { 61 | [PrimaryKey] 62 | public int ItemId { get; set; } 63 | } 64 | 65 | [MemoryTable("sequantial_master"), MessagePackObject(true)] 66 | public class SequentialCheckMaster : IValidatable 67 | { 68 | [PrimaryKey] 69 | public int Id { get; set; } 70 | public int Cost { get; set; } 71 | 72 | public void Validate(IValidator validator) 73 | { 74 | if (validator.CallOnce()) 75 | { 76 | var set = validator.GetTableSet(); 77 | 78 | set.Sequential(x => x.Id); 79 | set.Sequential(x => x.Cost, true); 80 | } 81 | } 82 | } 83 | 84 | [MemoryTable("single_master"), MessagePackObject(true)] 85 | public class SingleMaster : IValidatable 86 | { 87 | public static int CalledValidateCount; 88 | public static int CalledOnceCount; 89 | 90 | [PrimaryKey] 91 | public int Id { get; set; } 92 | 93 | public void Validate(IValidator validator) 94 | { 95 | CalledValidateCount++; 96 | if (validator.CallOnce()) 97 | { 98 | CalledOnceCount++; 99 | } 100 | } 101 | } 102 | 103 | [MemoryTable("fail"), MessagePackObject(true)] 104 | public class Fail : IValidatable 105 | { 106 | [PrimaryKey] 107 | public int Id { get; set; } 108 | 109 | public void Validate(IValidator validator) 110 | { 111 | validator.Fail("Failed Id:" + Id); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /tests/MasterMemory.Tests/TestStructures/Sample.cs: -------------------------------------------------------------------------------- 1 | using MessagePack; 2 | 3 | namespace MasterMemory.Tests 4 | { 5 | [MemoryTable("s_a_m_p_l_e"), MessagePackObject(true)] 6 | public class Sample 7 | { 8 | [PrimaryKey] 9 | [SecondaryKey(1)] 10 | [SecondaryKey(2)] 11 | [SecondaryKey(3)] 12 | public int Id { get; set; } 13 | [SecondaryKey(1)] 14 | [SecondaryKey(2)] 15 | [SecondaryKey(3)] 16 | [SecondaryKey(5), NonUnique] 17 | [SecondaryKey(6, 1), NonUnique] 18 | public int Age { get; set; } 19 | [SecondaryKey(0)] 20 | [SecondaryKey(1)] 21 | [SecondaryKey(3)] 22 | [SecondaryKey(4), NonUnique] 23 | [SecondaryKey(6, 0), NonUnique] 24 | public string FirstName { get; set; } 25 | [SecondaryKey(0)] 26 | [SecondaryKey(1)] 27 | public string LastName { get; set; } 28 | 29 | 30 | [MessagePack.IgnoreMember] 31 | public int Hoge { get; set; } 32 | 33 | [System.Runtime.Serialization.IgnoreDataMember] 34 | public int Huga { get; set; } 35 | 36 | public override string ToString() 37 | { 38 | return $"{Id} {Age} {FirstName} {LastName}"; 39 | } 40 | 41 | public Sample() 42 | { 43 | 44 | } 45 | 46 | public Sample(int Id, int Age, string FirstName, string LastName) 47 | { 48 | this.Id = Id; 49 | this.Age = Age; 50 | this.FirstName = FirstName; 51 | this.LastName = LastName; 52 | } 53 | 54 | 55 | } 56 | } -------------------------------------------------------------------------------- /tests/MasterMemory.Tests/TestStructures/SkillMaster.cs: -------------------------------------------------------------------------------- 1 | using MessagePack; 2 | 3 | namespace MasterMemory.Tests 4 | { 5 | [MemoryTable("skillmaster"), MessagePackObject(true)] 6 | public class SkillMaster 7 | { 8 | [PrimaryKey] 9 | public int SkillId { get; set; } 10 | [PrimaryKey] 11 | public int SkillLevel { get; set; } 12 | public int AttackPower { get; set; } 13 | public string SkillName { get; set; } 14 | public string Description { get; set; } 15 | 16 | public SkillMaster() 17 | { 18 | 19 | } 20 | 21 | public SkillMaster(int SkillId, int SkillLevel, int AttackPower, string SkillName, string Description) 22 | { 23 | this.SkillId = SkillId; 24 | this.SkillLevel = SkillLevel; 25 | this.AttackPower = AttackPower; 26 | this.SkillName = SkillName; 27 | this.Description = Description; 28 | } 29 | 30 | } 31 | } -------------------------------------------------------------------------------- /tests/MasterMemory.Tests/TestStructures/TestMaster.cs: -------------------------------------------------------------------------------- 1 | using MessagePack; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace MasterMemory.Tests.TestStructures 7 | { 8 | [MessagePackObject(true)] 9 | [MemoryTable(nameof(TestMaster))] 10 | public class TestMaster 11 | { 12 | [PrimaryKey, NonUnique] 13 | public int TestID { get; set; } 14 | public int Value { get; set; } 15 | 16 | public TestMaster(int TestID, int Value) 17 | { 18 | this.TestID = TestID; 19 | this.Value = Value; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/MasterMemory.Tests/TestStructures/UserLevel.cs: -------------------------------------------------------------------------------- 1 | using MessagePack; 2 | 3 | namespace MasterMemory.Tests 4 | { 5 | [MemoryTable("UserLevel"), MessagePackObject(true)] 6 | public class UserLevel 7 | { 8 | [PrimaryKey] 9 | public int Level { get; set; } 10 | [SecondaryKey(0)] 11 | public int Exp { get; set; } 12 | 13 | public UserLevel() 14 | { 15 | 16 | } 17 | 18 | public UserLevel(int Level, int Exp) 19 | { 20 | this.Level = Level; 21 | this.Exp = Exp; 22 | } 23 | 24 | } 25 | } --------------------------------------------------------------------------------