[
  {
    "path": ".editorconfig",
    "content": "﻿# top-most EditorConfig file\nroot = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_style = space\nindent_size = 2\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n# 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)\nspelling_exclusion_path  = ./exclusion.dic\n\n[*.cs]\nindent_size = 4\ncharset = utf-8-bom\nend_of_line = unset\n\n# Solution files\n[*.{sln,slnx}]\nend_of_line = unset\n\n# MSBuild project files\n[*.{csproj,props,targets}]\nend_of_line = unset\n\n# Xml config files\n[*.{ruleset,config,nuspec,resx,runsettings,DotSettings}]\nend_of_line = unset\n\n[*{_AssemblyInfo.cs,.notsupported.cs}]\ngenerated_code = true\n\n# C# code style settings\n[*.{cs}]\ndotnet_diagnostic.IDE0044.severity = none # IDE0044: Make field readonly\n\n# https://stackoverflow.com/questions/79195382/how-to-disable-fading-unused-methods-in-visual-studio-2022-17-12-0\ndotnet_diagnostic.IDE0051.severity = none # IDE0051: Remove unused private member\ndotnet_diagnostic.IDE0130.severity = none # IDE0130: Namespace does not match folder structure\n"
  },
  {
    "path": ".github/dependabot.yaml",
    "content": "# ref: https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot\nversion: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\" # Check for updates to GitHub Actions every week\n    groups:\n      dependencies:\n        patterns:\n          - \"*\"\n    cooldown:\n      default-days: 14 # Wait 14 days before creating another PR for the same dependency. This will prevent vulnerability on the package impact.\n    ignore:\n      # I just want update action when major/minor version is updated. patch updates are too noisy.\n      - dependency-name: \"*\"\n        update-types:\n          - version-update:semver-patch\n"
  },
  {
    "path": ".github/workflows/build-debug.yaml",
    "content": "name: Build-Debug\n\non:\n  push:\n    branches:\n      - \"master\"\n  pull_request:\n    branches:\n      - \"master\"\n\njobs:\n  build-dotnet:\n    permissions:\n      contents: read\n    runs-on: ubuntu-24.04\n    timeout-minutes: 15\n    steps:\n      - uses: Cysharp/Actions/.github/actions/checkout@main\n      - uses: Cysharp/Actions/.github/actions/setup-dotnet@main\n        with:\n          dotnet-version: |\n            9.0.x\n      - run: dotnet build -c Release\n      - run: dotnet test -c Release --no-build\n      - run: dotnet pack -c Release --no-build -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -o $GITHUB_WORKSPACE/artifacts\n"
  },
  {
    "path": ".github/workflows/build-release.yaml",
    "content": "name: Build-Release\n\non:\n  workflow_dispatch:\n    inputs:\n      tag:\n        description: \"tag: git tag you want create. (sample 1.0.0)\"\n        required: true\n      dry-run:\n        description: \"dry-run: true will never create relase/nuget.\"\n        required: true\n        default: false\n        type: boolean\n\njobs:\n  build-dotnet:\n    permissions:\n      contents: read\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    steps:\n      - uses: Cysharp/Actions/.github/actions/checkout@main\n      - uses: Cysharp/Actions/.github/actions/setup-dotnet@main\n      # pack nuget\n      - run: dotnet build -c Release -p:Version=${{ inputs.tag }}\n      - run: dotnet test -c Release --no-build\n      - run: dotnet pack -c Release --no-build -p:Version=${{ inputs.tag }} -o ./publish\n      - uses: Cysharp/Actions/.github/actions/upload-artifact@main\n        with:\n          name: nuget\n          path: ./publish\n          retention-days: 1\n\n  # release\n  create-release:\n    needs: [build-dotnet]\n    permissions:\n      contents: write\n      id-token: write # required for NuGet Trusted Publish\n    uses: Cysharp/Actions/.github/workflows/create-release.yaml@main\n    with:\n      commit-id: ${{ github.sha }}\n      dry-run: ${{ inputs.dry-run }}\n      tag: ${{ inputs.tag }}\n      nuget-push: true\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/prevent-github-change.yaml",
    "content": "name: Prevent github change\non:\n  pull_request:\n    paths:\n      - \".github/**/*.yaml\"\n      - \".github/**/*.yml\"\n\njobs:\n  detect:\n    permissions:\n      contents: read\n    uses: Cysharp/Actions/.github/workflows/prevent-github-change.yaml@main\n"
  },
  {
    "path": ".github/workflows/stale.yaml",
    "content": "name: \"Close stale issues\"\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron: \"0 0 * * *\"\n\njobs:\n  stale:\n    permissions:\n      contents: read\n      pull-requests: write\n      issues: write\n    uses: Cysharp/Actions/.github/workflows/stale-issue.yaml@main\n"
  },
  {
    "path": ".github/workflows/toc.yaml",
    "content": "name: TOC Generator\n\non:\n  push:\n    paths:\n      - 'README.md'\n\njobs:\n  toc:\n    permissions:\n      contents: write\n    uses: Cysharp/Actions/.github/workflows/toc-generator.yaml@main\n    with:\n      TOC_TITLE: \"## Table of Contents\"\n    secrets: inherit\n"
  },
  {
    "path": ".gitignore",
    "content": "# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)\n[Bb]in/\n[Oo]bj/\n\n# mstest test results\nTestResults\n\n## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User-specific files\n*.suo\n*.user\n*.sln.docstates\n\n# Build results\n[Dd]ebug/\n[Rr]elease/\nx64/\n*_i.c\n*_p.c\n*.ilk\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.log\n*.vspscc\n*.vssscc\n.builds\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opensdf\n*.sdf\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*\n\n# NCrunch\n*.ncrunch*\n.*crunch*.local.xml\n\n# Installshield output folder\n[Ee]xpress\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish\n\n# Publish Web Output\n*.Publish.xml\n\n# NuGet Packages Directory\n*.nupkg\n# NuGet Symbol Packages\n*.snupkg\n# The packages folder can be ignored because of Package Restore\n# packages # upm pacakge will use Packages\n# **/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n# !**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Windows Azure Build Output\ncsx\n*.build.csdef\n\n# Windows Store app package directory\nAppPackages/\n\n# Others\n[Bb]in\n[Oo]bj\nsql\nTestResults\n[Tt]est[Rr]esult*\n*.Cache\nClientBin\n[Ss]tyle[Cc]op.*\n~$*\n*.dbmdl\nGenerated_Code #added for RIA/Silverlight projects\n\n# Backup & report files from converting an old project file to a newer\n# Visual Studio version. Backup files are not needed, because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\n.vs/config/applicationhost.config\n.vs/restore.dg\n\n.vs\n.vsconfig\n\n# Unity\nsrc/MasterMemory.Unity/bin/*\nsrc/MasterMemory.Unity/Library/*\nsrc/MasterMemory.Unity/obj/*\nsrc/MasterMemory.Unity/Temp/*\nsrc/MasterMemory.Unity/[Uu]ser[Ss]ettings/\nsrc/MasterMemory.Unity/*.sln\nsrc/MasterMemory.Unity/*.csproj\nsrc/MasterMemory.Unity/*.unitypackage\n!src/MasterMemory.Unity/Packages/\n"
  },
  {
    "path": "Directory.Build.props",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup>\n    <LangVersion>13</LangVersion>\n\n    <!-- NuGet Package Information -->\n    <IsPackable>false</IsPackable>\n    <PackageVersion>$(Version)</PackageVersion>\n    <Company>Cysharp</Company>\n    <Authors>Cysharp</Authors>\n    <Copyright>© Cysharp, Inc.</Copyright>\n    <PackageTags>database, embedded, inmemory, unity</PackageTags>\n    <PackageProjectUrl>https://github.com/Cysharp/MasterMemory</PackageProjectUrl>\n    <PackageReadmeFile>README.md</PackageReadmeFile>\n    <RepositoryUrl>$(PackageProjectUrl)</RepositoryUrl>\n    <RepositoryType>git</RepositoryType>\n    <PackageLicenseExpression>MIT</PackageLicenseExpression>\n    <PackageIcon>Icon.png</PackageIcon>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <None Include=\"$(MSBuildThisFileDirectory)Icon.png\" Pack=\"true\" PackagePath=\"\\\" />\n    <None Include=\"$(MSBuildThisFileDirectory)README.md\" Pack=\"true\" PackagePath=\"\\\" />\n    <EmbeddedResource Include=\"$(MSBuildThisFileDirectory)LICENSE\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Yoshifumi Kawai / Cysharp, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "MasterMemory.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.12.35527.113\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"src\", \"src\", \"{60662102-4523-441E-8D6C-D87A3246C648}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"tests\", \"tests\", \"{BDAFF1CB-8E53-412B-B389-42A15343C7A3}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"sandbox\", \"sandbox\", \"{FFAA235C-D30F-4958-BC4E-60CD08979464}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"MasterMemory\", \"src\\MasterMemory\\MasterMemory.csproj\", \"{D2720BBB-C233-4A1E-9768-1F00C9602180}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"MasterMemory.Tests\", \"tests\\MasterMemory.Tests\\MasterMemory.Tests.csproj\", \"{8C5EBACA-C6C7-463B-B85C-C6A05E5DEB9F}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"MasterMemory.Annotations\", \"src\\MasterMemory.Annotations\\MasterMemory.Annotations.csproj\", \"{A13F40DD-7777-4E97-9FC4-6324722CA964}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Benchmark\", \"sandbox\\Benchmark\\Benchmark.csproj\", \"{205509EA-78C8-4ED0-B2B5-8030DDFB0BF0}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"ConsoleApp\", \"sandbox\\ConsoleApp\\ConsoleApp.csproj\", \"{2657C9C5-0BEA-4616-BE41-A19E8298C591}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"PerfTest2\", \"sandbox\\PerfTest2\\PerfTest2.csproj\", \"{AA5B5485-C42E-449C-843A-98A99A0D10B2}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"MasterMemory.SourceGenerator\", \"src\\MasterMemory.SourceGenerator\\MasterMemory.SourceGenerator.csproj\", \"{73F0ABAF-E55F-4E63-923B-4ABDE794D490}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"GeneratorSandbox\", \"sandbox\\GeneratorSandbox\\GeneratorSandbox.csproj\", \"{D1D2B635-99CC-4C90-BAAB-57D188B5BE42}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"MasterMemory.SourceGenerator.Tests\", \"tests\\MasterMemory.SourceGenerator.Tests\\MasterMemory.SourceGenerator.Tests.csproj\", \"{96F54302-35CD-4CDC-AAAC-8A6858DCAFEB}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{D2720BBB-C233-4A1E-9768-1F00C9602180}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{D2720BBB-C233-4A1E-9768-1F00C9602180}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{D2720BBB-C233-4A1E-9768-1F00C9602180}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{D2720BBB-C233-4A1E-9768-1F00C9602180}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{8C5EBACA-C6C7-463B-B85C-C6A05E5DEB9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{8C5EBACA-C6C7-463B-B85C-C6A05E5DEB9F}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{8C5EBACA-C6C7-463B-B85C-C6A05E5DEB9F}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{8C5EBACA-C6C7-463B-B85C-C6A05E5DEB9F}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{A13F40DD-7777-4E97-9FC4-6324722CA964}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{A13F40DD-7777-4E97-9FC4-6324722CA964}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{A13F40DD-7777-4E97-9FC4-6324722CA964}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{A13F40DD-7777-4E97-9FC4-6324722CA964}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{205509EA-78C8-4ED0-B2B5-8030DDFB0BF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{205509EA-78C8-4ED0-B2B5-8030DDFB0BF0}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{205509EA-78C8-4ED0-B2B5-8030DDFB0BF0}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{205509EA-78C8-4ED0-B2B5-8030DDFB0BF0}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{2657C9C5-0BEA-4616-BE41-A19E8298C591}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{2657C9C5-0BEA-4616-BE41-A19E8298C591}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{2657C9C5-0BEA-4616-BE41-A19E8298C591}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{2657C9C5-0BEA-4616-BE41-A19E8298C591}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{AA5B5485-C42E-449C-843A-98A99A0D10B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{AA5B5485-C42E-449C-843A-98A99A0D10B2}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{AA5B5485-C42E-449C-843A-98A99A0D10B2}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{AA5B5485-C42E-449C-843A-98A99A0D10B2}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{73F0ABAF-E55F-4E63-923B-4ABDE794D490}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{73F0ABAF-E55F-4E63-923B-4ABDE794D490}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{73F0ABAF-E55F-4E63-923B-4ABDE794D490}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{73F0ABAF-E55F-4E63-923B-4ABDE794D490}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{D1D2B635-99CC-4C90-BAAB-57D188B5BE42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{D1D2B635-99CC-4C90-BAAB-57D188B5BE42}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{D1D2B635-99CC-4C90-BAAB-57D188B5BE42}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{D1D2B635-99CC-4C90-BAAB-57D188B5BE42}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{96F54302-35CD-4CDC-AAAC-8A6858DCAFEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{96F54302-35CD-4CDC-AAAC-8A6858DCAFEB}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{96F54302-35CD-4CDC-AAAC-8A6858DCAFEB}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{96F54302-35CD-4CDC-AAAC-8A6858DCAFEB}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(NestedProjects) = preSolution\n\t\t{D2720BBB-C233-4A1E-9768-1F00C9602180} = {60662102-4523-441E-8D6C-D87A3246C648}\n\t\t{8C5EBACA-C6C7-463B-B85C-C6A05E5DEB9F} = {BDAFF1CB-8E53-412B-B389-42A15343C7A3}\n\t\t{A13F40DD-7777-4E97-9FC4-6324722CA964} = {60662102-4523-441E-8D6C-D87A3246C648}\n\t\t{205509EA-78C8-4ED0-B2B5-8030DDFB0BF0} = {FFAA235C-D30F-4958-BC4E-60CD08979464}\n\t\t{2657C9C5-0BEA-4616-BE41-A19E8298C591} = {FFAA235C-D30F-4958-BC4E-60CD08979464}\n\t\t{AA5B5485-C42E-449C-843A-98A99A0D10B2} = {FFAA235C-D30F-4958-BC4E-60CD08979464}\n\t\t{73F0ABAF-E55F-4E63-923B-4ABDE794D490} = {60662102-4523-441E-8D6C-D87A3246C648}\n\t\t{D1D2B635-99CC-4C90-BAAB-57D188B5BE42} = {FFAA235C-D30F-4958-BC4E-60CD08979464}\n\t\t{96F54302-35CD-4CDC-AAAC-8A6858DCAFEB} = {BDAFF1CB-8E53-412B-B389-42A15343C7A3}\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {0121A3C4-6AE0-4622-BE04-05D9A3E729AC}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "README.md",
    "content": "[![GitHub Actions](https://github.com/Cysharp/MasterMemory/workflows/Build-Debug/badge.svg)](https://github.com/Cysharp/MasterMemory/actions) [![Releases](https://img.shields.io/github/release/Cysharp/MasterMemory.svg)](https://github.com/Cysharp/MasterMemory/releases)\n\nMasterMemory\n===\nSource Generator based Embedded Typed Readonly In-Memory Document Database for .NET and Unity. \n\n![image](https://user-images.githubusercontent.com/46207/61031896-61890800-a3fb-11e9-86b7-84c821d347a4.png)\n\n**4700** times faster than SQLite and achieves zero allocation per query. Also the DB size is small. When SQLite is 3560kb then MasterMemory is only 222kb.\n\nSource Generator automatically generates a typed database structure from schemas (classes), which ensures that all queries are type-safe with full autocompletion support.\n\n![image](https://github.com/user-attachments/assets/e804fa52-f6a5-4972-a510-0b3b17a31230)\n\n![image](https://user-images.githubusercontent.com/46207/61035808-cb58e000-a402-11e9-9209-d51665d1cd56.png)\n\nThis ensures both optimal performance and excellent usability.\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n## Table of Contents\n\n- [Concept](#concept)\n- [Getting Started(.NET)](#getting-startednet)\n- [Getting Started(Unity)](#getting-startedunity)\n- [DataTable configuration](#datatable-configuration)\n- [MemoryDatabase/RangeView](#memorydatabaserangeview)\n- [Extend Table](#extend-table)\n- [ImmutableBuilder](#immutablebuilder)\n- [Validator](#validator)\n- [Metadata](#metadata)\n- [Inheritance](#inheritance)\n- [Optimization](#optimization)\n- [MasterMemoryGeneratorOptions](#mastermemorygeneratoroptions)\n- [v2 -> v3 migration](#v2---v3-migration)\n- [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\nConcept\n---\n\n* **Memory Efficient**, Only use underlying data memory and do aggressively string interning.\n* **Performance**, Similar as dictionary lookup.\n* **TypeSafe**, 100% Type safe by Source Generator.\n* **Fast load speed**,  MasterMemory save data by [MessagePack for C#, a fastest C# serializer](https://github.com/neuecc/MessagePack-CSharp) so load speed is blazing fast.\n* **Flexible Search**, Supports multiple key, multiple result, range/closest query.\n* **Validator**, You can define custom data validation by C#.\n* **Metadata**, To make custom importer/exporter, get the all database metadata.\n\nThese features are suitable for master data management(write-once, read-heavy) on embedded application, data analysis, game, etc. MasterMemory has better performance than any other database solutions. [PalDB](https://github.com/linkedin/PalDB) developed by LinkedIn has a similar concept(embeddable write-once key-value store), but the implementation and performance characteristics are completely different.\n\nGetting Started(.NET)\n---\nInstall the [MasterMemory](https://www.nuget.org/packages/MasterMemory) library(Runtime, Source Generator(Analyzer) via NuGet.\n\n```\ndotnet add package MasterMemory\n```\n\nPrepare the example table definition like following.\n\n```csharp\npublic enum Gender\n{\n    Male, Female, Unknown\n}\n\n// table definition marked by MemoryTableAttribute.\n// database-table must be serializable by MessagePack-CSsharp\n[MemoryTable(\"person\"), MessagePackObject(true)]\npublic record Person\n{\n    // index definition by attributes.\n    [PrimaryKey]\n    public required int PersonId { get; init; }\n\n    // secondary index can add multiple(discriminated by index-number).\n    [SecondaryKey(0), NonUnique]\n    [SecondaryKey(1, keyOrder: 1), NonUnique]\n    public required int Age { get; init; }\n\n    [SecondaryKey(2), NonUnique]\n    [SecondaryKey(1, keyOrder: 0), NonUnique]\n    public required Gender Gender { get; init; }\n\n    public required string Name { get; init; }\n}\n```\n\nData in MasterMemory is readonly, so it is recommended to use an immutable structure. While both records and classes are supported, records might be preferable as they generate more readable ToString methods.\n\nMasterMemory's Source Generator detects types marked with the `MemoryTable` attribute and automatically generates types like the following:\n\n![image](https://github.com/user-attachments/assets/e804fa52-f6a5-4972-a510-0b3b17a31230)\n\nFinally, you can regsiter and query by these files.\n\n```csharp\nusing ...; // Your project default namespace\n\n// to create database, use DatabaseBuilder and Append method.\nvar builder = new DatabaseBuilder();\nbuilder.Append(new Person[]\n{\n    new (){ PersonId = 0, Age = 13, Gender = Gender.Male,   Name = \"Dana Terry\" },\n    new (){ PersonId = 1, Age = 17, Gender = Gender.Male,   Name = \"Kirk Obrien\" },\n    new (){ PersonId = 2, Age = 31, Gender = Gender.Male,   Name = \"Wm Banks\" },\n    new (){ PersonId = 3, Age = 44, Gender = Gender.Male,   Name = \"Karl Benson\" },\n    new (){ PersonId = 4, Age = 23, Gender = Gender.Male,   Name = \"Jared Holland\" },\n    new (){ PersonId = 5, Age = 27, Gender = Gender.Female, Name = \"Jeanne Phelps\" },\n    new (){ PersonId = 6, Age = 25, Gender = Gender.Female, Name = \"Willie Rose\" },\n    new (){ PersonId = 7, Age = 11, Gender = Gender.Female, Name = \"Shari Gutierrez\" },\n    new (){ PersonId = 8, Age = 63, Gender = Gender.Female, Name = \"Lori Wilson\" },\n    new (){ PersonId = 9, Age = 34, Gender = Gender.Female, Name = \"Lena Ramsey\" },\n});\n\n// build database binary(you can also use `WriteToStream` for save to file).\nbyte[] data = builder.Build();\n\n// -----------------------\n\n// for query phase, create MemoryDatabase.\n// (MemoryDatabase is recommended to store in singleton container(static field/DI)).\nvar db = new MemoryDatabase(data);\n\n// .PersonTable.FindByPersonId is fully typed by code-generation.\nPerson person = db.PersonTable.FindByPersonId(5);\n\n// Multiple key is also typed(***And * **), Return value is multiple if key is marked with `NonUnique`.\nRangeView<Person> result = db.PersonTable.FindByGenderAndAge((Gender.Female, 23));\n\n// Get nearest value(choose lower(default) or higher).\nRangeView<Person> age1 = db.PersonTable.FindClosestByAge(31);\n\n// Get range(min-max inclusive).\nRangeView<Person> age2 = db.PersonTable.FindRangeByAge(20, 29);\n```\n\nAll table(marked by `MemoryTableAttribute`) and methods(created by `PrimaryKeyAttribute` or `SecondaryKeyAttribute`) are typed.\n\n![image](https://user-images.githubusercontent.com/46207/61035808-cb58e000-a402-11e9-9209-d51665d1cd56.png)\n\nYou can invoke all indexed query by IntelliSense.\n\nGetting Started(Unity)\n---\nThe minimum supported Unity version will be `2022.3.12f1`, as it is necessary to support C# Incremental Source Generator(Compiler Version, 4.3.0).\n\nSince this library is provided via NuGet, install [NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity), then navigate to Open Window from NuGet -> Manage NuGet Packages, Search \"MasterMemory\" and Press Install.\n\nFirst, it is recommended to define assembly attributes in any cs file to enable the use of `init`.\n\n```csharp\n// Optional: Unity can't load default namespace to Source Generator\n// If not specified, 'MasterMemory' will be used by default,\n// but you can use this attribute if you want to specify a different namespace.\n[assembly: MasterMemoryGeneratorOptions(Namespace = \"MyProj\")]\n\n// Optional: If you want to use init keyword, copy-and-paste this.\nnamespace System.Runtime.CompilerServices\n{\n    internal sealed class IsExternalInit { }\n}\n```\n\nEverything else is the same as the standard .NET version. While the `required` keyword can't be used since it's from C# 11, using `init` alone is sufficient to guarantee immutability.\n\n```csharp\npublic enum Gender\n{\n    Male, Female, Unknown\n}\n\n// table definition marked by MemoryTableAttribute.\n// database-table must be serializable by MessagePack-CSsharp\n[MemoryTable(\"person\"), MessagePackObject(true)]\npublic record Person\n{\n    // index definition by attributes.\n    [PrimaryKey]\n    public int PersonId { get; init; }\n\n    // secondary index can add multiple(discriminated by index-number).\n    [SecondaryKey(0), NonUnique]\n    [SecondaryKey(1, keyOrder: 1), NonUnique]\n    public int Age { get; init; }\n\n    [SecondaryKey(2), NonUnique]\n    [SecondaryKey(1, keyOrder: 0), NonUnique]\n    public Gender Gender { get; init; }\n\n    public string Name { get; init; }\n}\n```\n\nAlso, for use with IL2CPP, you need to add the generated `MasterMemoryResolver` to MessagePack's Resolver. If you need other generated Resolvers, such as those from [MagicOnion](https://github.com/Cysharp/MagicOnion), please add and compose them here.\n\n```csharp\npublic static class Initializer\n{\n    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]\n    public static void SetupMessagePackResolver()\n    {\n        // Create CompositeResolver\n        StaticCompositeResolver.Instance.Register(new[]{\n            MasterMemoryResolver.Instance, // set MasterMemory generated resolver\n            StandardResolver.Instance      // set default MessagePack resolver\n        });\n\n        // Create options with resolver\n        var options = MessagePackSerializerOptions.Standard.WithResolver(StaticCompositeResolver.Instance);\n\n        // Optional: as default.\n        MessagePackSerializer.DefaultOptions = options;\n    }\n}\n```\n\nDataTable configuration\n---\nElement type of datatable must be marked by `[MemoryTable(tableName)]`, datatable is generated from marked type. `string tableName` is saved in database binary, you can rename class name if tableName is same.\n\n`[PrimaryKey(keyOrder = 0)]`, `[SecondaryKey(indexNo, keyOrder)]`, `[NonUnique]` can add to public property, `[PrimaryKey]` must use in MemoryTable, `[SecondaryKey]` is option.\n\nBoth `PrimaryKey` and `SecondaryKey` can add to multiple properties, it will be generated `***And***And***...`. `keyOrder` is order of column names, default is zero(sequential in which they appear).\n\n```csharp\n[MemoryTable(\"sample\"), MessagePackObject(true)]\npublic class Sample\n{\n    [PrimaryKey]\n    public int Foo { get; set; }\n    [PrimaryKey]\n    public int Bar { get; set; }\n}\n\ndb.Sample.FindByFooAndBar((int Foo, int Bar))\n\n// ----\n\n[MemoryTable(\"sample\"), MessagePackObject(true)]\npublic class Sample\n{\n    [PrimaryKey(keyOrder: 1)]\n    public int Foo { get; set; }\n    [PrimaryKey(keyOrder: 0)]\n    public int Bar { get; set; }\n}\n\ndb.Sample.FindByBarAndFoo((int Bar, int Foo))\n```\n\nDefault of `FindBy***` return type is single(if not found, returns `null`). It means key is unique by default. If mark `[NonUnique]` in same AttributeList, return type is `RangeView<T>`(if not found, return empty).\n\n```csharp\n[MemoryTable(\"sample\"), MessagePackObject(true)]\npublic class Sample\n{\n    [PrimaryKey, NonUnique]\n    public int Foo { get; set; }\n    [PrimaryKey, NonUnique]\n    public int Bar { get; set; }\n}\n\nRangeView<Sample> q = db.Sample.FindByFooAndBar((int Foo, int Bar))\n```\n\n```csharp\n[MemoryTable(\"sample\"), MessagePackObject(true)]\npublic class Sample\n{\n    [PrimaryKey]\n    [SecondaryKey(0)]\n    public int Foo { get; set; }\n    [SecondaryKey(0)]\n    [SecondaryKey(1)]\n    public int Bar { get; set; }\n}\n\ndb.Sample.FindByFoo(int Foo)\ndb.Sample.FindByFooAndBar((int Foo, int Bar))\ndb.Sample.FindByBar(int Bar)\n```\n\n`[StringComparisonOption]` allow to configure how compare if key is string. Default is `Ordinal`.\n\n```csharp\n[MemoryTable(\"sample\"), MessagePackObject(true)]\npublic class Sample\n{\n    [PrimaryKey]\n    [StringComparisonOption(StringComparison.InvariantCultureIgnoreCase)]\n    public string Foo { get; set; }\n}\n```\n\nIf computation property exists, add `[IgnoreMember]` of MessagePack should mark.\n\n```csharp\n[MemoryTable(\"person\"), MessagePackObject(true)]\npublic class Person\n{\n    [PrimaryKey]\n    public int Id { get;}\n\n    public string FirstName { get; }\n    public string LastName { get; }\n\n    [IgnoreMember]\n    public string FullName => FirstName + LastName;\n}\n```\n\nMemoryDatabase/RangeView\n---\nIn default, `MemoryDatabase` do all string data automatically interning(see: [Wikipedia/String interning](https://en.wikipedia.org/wiki/String_interning)). If multiple same string value exists in database(ex: \"goblin\",\"goblin\", \"goblin\", \"goblin\", \"goblin\"....), standard database creates string value per query or store multiple same values. But MasterMemory stores single string value reference, it can save much memory if data is denormalized.\n\nUse intern or not is selected in constructor. If you want to disable automatically interning, use `internString:false`.\n\n`MemoryDatabase(byte[] databaseBinary, bool internString = true, MessagePack.IFormatterResolver formatterResolver = null, int maxDegreeOfParallelism = 1)`.\n\nMemoryDatabase has three(or four) query methods.\n\n* `T|RangeView<T>` FindBy***(TKey key)\n* bool TryFindBy***(TKey key, out T result)\n* `T|RangeView<T>` FindClosestBy***(TKey key, bool selectLower = true)\n* `RangeView<T>` FindRangeBy***(TKey min, TKey max, bool ascendant = true)\n\nIf index key is unique, generates `FindBy***` and `TryFindBy***` methods and then `FindBy***` throws `KeyNotFoundException` when key is not found.\n\n`By***` is generated by `PrimaryKey` and `SecondaryKey` defines.\n\nAnd has some utility properties.\n\n* `int` Count\n* `RangeView<T>` All\n* `RangeView<T>` AllReverse\n* `RangeView<T>` SortBy***\n* `T[] GetRawDataUnsafe()`\n\n`struct RangeView<T> : IEnumerable<T>` is the view of database elements. It has following property/method.\n\n* `T` [int index]\n* `int` Count\n* `T` First\n* `T` Last\n* `RangeView<T>` Reverse\n* `IEnumerator<T>` GetEnumerator()\n\nExtend Table\n---\nGenerated table class is defined partial class so create same namespace and class name's partial class on another file, you can add your custom method to generated table.\n\nTable class also defined partial `OnAfterConstruct` method, it called after table has been constructed. You can use it to store custom data to field after all data has been constructed.\n\n```csharp\n// create MonsterTable.Partial.cs\n\npublic sealed partial class MonsterTable\n{\n    int maxHp;\n#pragma warning disable CS0649\n    readonly int minHp;\n#pragma warning restore CS0649    \n\n    // called after constructed\n    partial void OnAfterConstruct()\n    {\n        maxHp = All.Select(x => x.MaxHp).Max();\n        // you can use Unsafe.AsRef to set readonly field\n        Unsafe.AsRef(minHp) = All.Select(x => x.MaxHp).Min();\n    }\n    \n    // add custom method other than standard Find method\n    public IEnumerable<Monster> GetRangedMonster(int arg1)\n    {\n        return All.Where....();\n    }\n}\n```\n\nImmutableBuilder\n---\nIf you want to add/modify data to loaded database, you can use `ToImmutableBuilder` method.\n\n```csharp\n// Create ImmutableBuilder from original database.\nvar builder = db.ToImmutableBuilder();\n\n// Add Or Replace compare with PrimaryKey\nbuilder.Diff(addOrReplaceData);\n\n// Remove by PrimaryKey\nbuilder.RemovePerson(new[] { 1, 10, 100 });\n\n// Replace all data\nbuilder.ReplaceAll(newData);\n\n// Finally create new database\nMemoryDatabase newDatabase = builder.Build();\n\n// If you want to save new database, you can convert to MemoryDatabase->DatabaseBuilder\nvar newBuilder = newDatabase.ToDatabaseBuilder();\nvar newBinary = newBuilder.Build(); // or use WriteToStream\n```\n\nMemoryDatabase's reference can use as snapshot.\n\n```csharp\n// 1 game per 1 instance\npublic class GameRoom\n{\n    MemoryDatabase database;\n\n    // The reference is a snapshot of the timing of game begins.\n    public GameRoom(MemoryDatabase database)\n    {\n        this.database = database;\n    }\n}\n```\n\nValidator\n---\nYou can validate data by `MemoryDatabase.Validate` method. In default, it check unique key(data duplicated) and you can define custom validate logics.\n\n```csharp\n// Implements IValidatable<T> to targeted validation\n[MemoryTable(\"quest_master\"), MessagePackObject(true)]\npublic class Quest : IValidatable<Quest>\n{\n    // If index is Unique, validate duplicate in default.\n    [PrimaryKey]\n    public int Id { get; }\n    public string Name { get; }\n    public int RewardId { get; }\n    public int Cost { get; }\n\n    void IValidatable<Quest>.Validate(IValidator<Quest> validator)\n    {\n        // get the external reference table\n        var items = validator.GetReferenceSet<Item>();\n\n        // Custom if logics.\n        if (this.RewardId > 0)\n        {\n            // RewardId must exists in Item.ItemId\n            items.Exists(x => x.RewardId, x => x.ItemId);\n        }\n\n        // Range check, Cost must be 10..20\n        validator.Validate(x => x.Cost >= 10);\n        validator.Validate(x => x.Cost <= 20);\n\n        // In this region, only called once so enable to validate overall of tables.\n        if (validator.CallOnce())\n        {\n            var quests = validator.GetTableSet();\n            // Check unique othe than index property.\n            quests.Where(x => x.RewardId != 0).Unique(x => x.RewardId);\n        }\n    }\n}\n\n[MemoryTable(\"item_master\"), MessagePackObject(true)]\npublic class Item\n{\n    [PrimaryKey]\n    public int ItemId { get; }\n}\n\nvoid Main()\n{\n    var db = new MemoryDatabase(bin);\n\n    // Get the validate result.\n    var validateResult = db.Validate();\n    if (validateResult.IsValidationFailed)\n    {\n        // Output string format.\n        Console.WriteLine(validateResult.FormatFailedResults());\n\n        // Get the raw FaildItem[]. (.Type, .Message, .Data)\n        // validateResult.FailedResults\n    }\n}\n```\n\nFollowing is list of validation methods.\n\n```csharp\n// all void methods are assert function, it stores message to ValidateResult if failed.\ninterface IValidator<T>\n{\n    ValidatableSet<T> GetTableSet();\n    ReferenceSet<T, TRef> GetReferenceSet<TRef>();\n    void Validate(Expression<Func<T, bool>> predicate);\n    void Validate(Func<T, bool> predicate, string message);\n    void ValidateAction(Expression<Func<bool>> predicate);\n    void ValidateAction(Func<bool> predicate, string message);\n    void Fail(string message);\n    bool CallOnce();\n}\n\nclass ReferenceSet<TElement, TReference>\n{\n    IReadOnlyList<TReference> TableData { get; }\n    void Exists<TProperty>(Expression<Func<TElement, TProperty>> elementSelector, Expression<Func<TReference, TProperty>> referenceElementSelector);\n    void Exists<TProperty>(Expression<Func<TElement, TProperty>> elementSelector, Expression<Func<TReference, TProperty>> referenceElementSelector, EqualityComparer<TProperty> equalityComparer);\n}\n\nclass ValidatableSet<TElement>\n{\n    IReadOnlyList<TElement> TableData { get; }\n    void Unique<TProperty>(Expression<Func<TElement, TProperty>> selector);\n    void Unique<TProperty>(Expression<Func<TElement, TProperty>> selector, IEqualityComparer<TProperty> equalityComparer);\n    void Unique<TProperty>(Func<TElement, TProperty> selector, string message);\n    void Unique<TProperty>(Func<TElement, TProperty> selector, IEqualityComparer<TProperty> equalityComparer, string message);\n    void Sequential(Expression<Func<TElement, SByte|Int16|Int32|...>> selector, bool distinct = false);\n    ValidatableSet<TElement> Where(Func<TElement, bool> predicate);\n}\n```\n\nMetadata\n---\nYou can get the table-info, properties, indexes by metadata api. It helps to make custom importer/exporter application.\n\n```csharp\nvar metaDb = MemoryDatabase.GetMetaDatabase();\nforeach (var table in metaDb.GetTableInfos())\n{\n    // for example, generate CSV header\n    var sb = new StringBuilder();\n    foreach (var prop in table.Properties)\n    {\n        if (sb.Length != 0) sb.Append(\",\");\n\n        // Name can convert to LowerCamelCase or SnakeCase.\n        sb.Append(prop.NameSnakeCase);\n    }\n    File.WriteAllText(table.TableName + \".csv\", sb.ToString(), new UTF8Encoding(false));\n}\n```\n\nIf creates console-app, our [ConsoleAppFramework](https://github.com/Cysharp/ConsoleAppFramework/) can easy to make helper applications.\n\nHere is sample of reading and creating dynamic from csv. `builder.AppendDynamic` and `System.Runtime.Serialization.FormatterServices.GetUninitializedObject` will help it.\n\n```csharp\nvar csv = @\"monster_id,name,max_hp\n1,foo,100\n2,bar,200\";\nvar fileName = \"monster\";\n\nvar builder = new DatabaseBuilder();\n\nvar meta = MemoryDatabase.GetMetaDatabase();\nvar table = meta.GetTableInfo(fileName);\n\nvar tableData = new List<object>();\n\nusing (var ms = new MemoryStream(Encoding.UTF8.GetBytes(csv)))\nusing (var sr = new StreamReader(ms, Encoding.UTF8))\nusing (var reader = new TinyCsvReader(sr))\n{\n    while ((reader.ReadValuesWithHeader() is Dictionary<string, string> values))\n    {\n        // create data without call constructor\n        // use System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject instead on .NET 8\n        var data = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(table.DataType);\n\n        foreach (var prop in table.Properties)\n        {\n            if (values.TryGetValue(prop.NameSnakeCase, out var rawValue))\n            {\n                var value = ParseValue(prop.PropertyInfo.PropertyType, rawValue);\n                if (prop.PropertyInfo.SetMethod == null)\n                {\n                    throw new Exception(\"Target property does not exists set method. If you use {get;}, please change to { get; private set; }, Type:\" + prop.PropertyInfo.DeclaringType + \" Prop:\" + prop.PropertyInfo.Name);\n                }\n                prop.PropertyInfo.SetValue(data, value);\n            }\n            else\n            {\n                throw new KeyNotFoundException($\"Not found \\\"{prop.NameSnakeCase}\\\" in \\\"{fileName}.csv\\\" header.\");\n            }\n        }\n\n        tableData.Add(data);\n    }\n}\n\n// add dynamic collection.\nbuilder.AppendDynamic(table.DataType, tableData);\n\nvar bin = builder.Build();\nvar database = new MemoryDatabase(bin);\n\n    static object ParseValue(Type type, string rawValue)\n    {\n        if (type == typeof(string)) return rawValue;\n\n        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))\n        {\n            if (string.IsNullOrWhiteSpace(rawValue)) return null;\n            return ParseValue(type.GenericTypeArguments[0], rawValue);\n        }\n\n        if (type.IsEnum)\n        {\n            var value = Enum.Parse(type, rawValue);\n            return value;\n        }\n\n        switch (Type.GetTypeCode(type))\n        {\n            case TypeCode.Boolean:\n                // True/False or 0,1\n                if (int.TryParse(rawValue, out var intBool))\n                {\n                    return Convert.ToBoolean(intBool);\n                }\n                return Boolean.Parse(rawValue);\n            case TypeCode.Char:\n                return Char.Parse(rawValue);\n            case TypeCode.SByte:\n                return SByte.Parse(rawValue, CultureInfo.InvariantCulture);\n            case TypeCode.Byte:\n                return Byte.Parse(rawValue, CultureInfo.InvariantCulture);\n            case TypeCode.Int16:\n                return Int16.Parse(rawValue, CultureInfo.InvariantCulture);\n            case TypeCode.UInt16:\n                return UInt16.Parse(rawValue, CultureInfo.InvariantCulture);\n            case TypeCode.Int32:\n                return Int32.Parse(rawValue, CultureInfo.InvariantCulture);\n            case TypeCode.UInt32:\n                return UInt32.Parse(rawValue, CultureInfo.InvariantCulture);\n            case TypeCode.Int64:\n                return Int64.Parse(rawValue, CultureInfo.InvariantCulture);\n            case TypeCode.UInt64:\n                return UInt64.Parse(rawValue, CultureInfo.InvariantCulture);\n            case TypeCode.Single:\n                return Single.Parse(rawValue, CultureInfo.InvariantCulture);\n            case TypeCode.Double:\n                return Double.Parse(rawValue, CultureInfo.InvariantCulture);\n            case TypeCode.Decimal:\n                return Decimal.Parse(rawValue, CultureInfo.InvariantCulture);\n            case TypeCode.DateTime:\n                return DateTime.Parse(rawValue, CultureInfo.InvariantCulture);\n            default:\n                if (type == typeof(DateTimeOffset))\n                {\n                    return DateTimeOffset.Parse(rawValue, CultureInfo.InvariantCulture);\n                }\n                else if (type == typeof(TimeSpan))\n                {\n                    return TimeSpan.Parse(rawValue, CultureInfo.InvariantCulture);\n                }\n                else if (type == typeof(Guid))\n                {\n                    return Guid.Parse(rawValue);\n                }\n\n                // or other your custom parsing.\n                throw new NotSupportedException();\n        }\n    }\n\n    // Non string escape, tiny reader with header.\n    public class TinyCsvReader : IDisposable\n    {\n        static char[] trim = new[] { ' ', '\\t' };\n\n        readonly StreamReader reader;\n        public IReadOnlyList<string> Header { get; private set; }\n\n        public TinyCsvReader(StreamReader reader)\n        {\n            this.reader = reader;\n            {\n                var line = reader.ReadLine();\n                if (line == null) throw new InvalidOperationException(\"Header is null.\");\n\n                var index = 0;\n                var header = new List<string>();\n                while (index < line.Length)\n                {\n                    var s = GetValue(line, ref index);\n                    if (s.Length == 0) break;\n                    header.Add(s);\n                }\n                this.Header = header;\n            }\n        }\n\n        string GetValue(string line, ref int i)\n        {\n            var temp = new char[line.Length - i];\n            var j = 0;\n            for (; i < line.Length; i++)\n            {\n                if (line[i] == ',')\n                {\n                    i += 1;\n                    break;\n                }\n                temp[j++] = line[i];\n            }\n\n            return new string(temp, 0, j).Trim(trim);\n        }\n\n        public string[] ReadValues()\n        {\n            var line = reader.ReadLine();\n            if (line == null) return null;\n            if (string.IsNullOrWhiteSpace(line)) return null;\n\n            var values = new string[Header.Count];\n            var lineIndex = 0;\n            for (int i = 0; i < values.Length; i++)\n            {\n                var s = GetValue(line, ref lineIndex);\n                values[i] = s;\n            }\n            return values;\n        }\n\n        public Dictionary<string, string> ReadValuesWithHeader()\n        {\n            var values = ReadValues();\n            if (values == null) return null;\n\n            var dict = new Dictionary<string, string>();\n            for (int i = 0; i < values.Length; i++)\n            {\n                dict.Add(Header[i], values[i]);\n            }\n\n            return dict;\n        }\n\n        public void Dispose()\n        {\n            reader.Dispose();\n        }\n    }\n}\n```\n\nInheritance\n---\nCurrently MasterMemory does not support inheritance. Recommend way to create common method, use interface and extension method. But if you want to create common method with common cached field(made by `OnAfterConstruct`), for workaround, create abstract class and all data properties to abstract.\n\n```csharp\npublic abstract class FooAndBarBase\n{\n    // all data properties to virtual\n    public virtual int Prop1 { get; protected set; }\n    public virtual int Prop2 { get; protected set; }\n\n    [IgnoreMember]\n    public int Prop3 => Prop1 + Prop2;\n\n    public IEnumerable<FooAndBarBase> CommonMethod()\n    {\n        throw new NotImplementedException();\n    }\n}\n\n[MemoryTable(\"foo_table\"), MessagePackObject(true)]\npublic class FooTable : FooAndBarBase\n{\n    [PrimaryKey]\n    public override int Prop1 { get; protected set; }\n    public override int Prop2 { get; protected set; }\n}\n\n[MemoryTable(\"bar_table\"), MessagePackObject(true)]\npublic class BarTable : FooAndBarBase\n{\n    [PrimaryKey]\n    public override int Prop1 { get; protected set; }\n    public override int Prop2 { get; protected set; }\n}\n```\n\nOptimization\n---\nWhen invoking `new MemoryDatabase(byte[] databaseBinary...)`, read and construct database from binary. If binary size is large then construct performance will slow down. `MemoryDatabase` has `ctor(..., int maxDegreeOfParallelism = 1)` option in constructor to construct in parallel.\n\n```csharp\nvar database = new MemoryDatabase(bin, maxDegreeOfParallelism: Environment.ProcessorCount);\n```\n\nThe use of Parallel can greatly improve the construct performance. Recommend to use `Environment.ProcessorCount`.\n\nIf you want to reduce code size of generated code, Validator and MetaDatabase info can omit in runtime. Generated code has two symbols `DISABLE_MASTERMEMORY_VALIDATOR` and `DISABLE_MASTERMEMORY_METADATABASE`.  By defining them, can be erased from the build code.\n\nThe database generation/loading speed and size are affected by MessagePack's serialization format. Using `[MessagePackObject]` with `[Key]` attributes instead of `[MessagePackObject(true)]` can improve loading speed and reduce size. However, regarding size, since LZ4 compression is used by default, the difference may not be significant.\n\nMasterMemoryGeneratorOptions\n---\nThe Source Generator settings are configured using the assembly attribute `[MasterMemoryGeneratorOptions]`. By placing it in any file, you can configure the following settings.\n\n```csharp\n[assembly: MasterMemoryGeneratorOptions(\n    Namespace = \"MyConsoleApp\",\n    IsReturnNullIfKeyNotFound = true,\n    PrefixClassName = \"Foo\"\n)]\n```\n\n* `Namespace`: Changes the namespace of generated files. If not specified, it tries to get the `RootNamespace` set in the csproj file; if that's not available, it defaults to `MasterMemory`\n* `IsReturnNullIfKeyNotFound`: By default, the `Find` method throws a `KeyNotFoundException` when a key is not found. If set to true, the return type becomes `T?` and returns null instead\n* `PrefixClassName`: Adds a prefix to the class names of generated files. For example, `DatabaseBuilder` becomes `FooDatabaseBuilder`. This allows you to distinguish between multiple MasterMemory projects by name.\n\nv2 -> v3 migration\n---\nSince there are no changes to the API, binary format, or behavior, you can migrate simply by changing the command-line tool settings to the assembly attribute `[MasterMemoryGeneratorOptions]`.\n\n* The code generator (MSBuild Task, .NET Core Global/Local Tools) has been removed and replaced with Source Generator\n* Tool options are now available through `MasterMemoryGeneratorOptions` (e.g. `-usingNamespace`)\n* The `-addImmutableConstructor` option has been completely removed; please use C#'s record or init keyword instead\n* The library is now only available through NuGet for Unity. Please use NuGetForUnity\n\nLicense\n---\nThis library is under the MIT License.\n"
  },
  {
    "path": "sandbox/Benchmark/Benchmark.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<OutputType>Exe</OutputType>\n\t\t<TargetFramework>net6.0</TargetFramework>\n\t\t<NoWarn>1701;1702;NU1904</NoWarn>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<PackageReference Include=\"BenchmarkDotNet\" Version=\"0.11.5\" />\n\t\t<PackageReference Include=\"EnyimMemcachedCore\" Version=\"2.2.4\" />\n\t\t<PackageReference Include=\"FASTER\" Version=\"2019.4.24.4\" />\n\t\t<PackageReference Include=\"LiteDB\" Version=\"4.1.4\" />\n\t\t<PackageReference Include=\"RocksDbNative\" Version=\"5.17.2\" />\n\t\t<PackageReference Include=\"RocksDbSharp\" Version=\"5.17.2\" />\n\t\t<PackageReference Include=\"System.Data.SQLite\" Version=\"1.0.111\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<ProjectReference Include=\"..\\..\\src\\MasterMemory.Annotations\\MasterMemory.Annotations.csproj\" />\n\t\t<ProjectReference Include=\"..\\..\\src\\MasterMemory\\MasterMemory.csproj\" />\n\t\t<ProjectReference Include=\"..\\..\\src\\MasterMemory.SourceGenerator\\MasterMemory.SourceGenerator.csproj\">\n\t\t\t<OutputItemType>Analyzer</OutputItemType>\n\t\t\t<ReferenceOutputAssembly>false</ReferenceOutputAssembly>\n\t\t</ProjectReference>\n\t</ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "sandbox/Benchmark/Program.cs",
    "content": "﻿#pragma warning disable\n\nusing BenchmarkDotNet.Attributes;\nusing System.Linq;\nusing BenchmarkDotNet.Configs;\nusing BenchmarkDotNet.Diagnosers;\nusing BenchmarkDotNet.Environments;\nusing BenchmarkDotNet.Exporters;\nusing BenchmarkDotNet.Exporters.Csv;\nusing BenchmarkDotNet.Jobs;\nusing BenchmarkDotNet.Running;\nusing LiteDB;\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.SQLite;\nusing System.IO;\nusing TestPerfLiteDB;\nusing Enyim.Caching;\nusing Enyim.Caching.Configuration;\nusing Enyim.Caching.Memcached;\nusing Microsoft.Extensions.Options;\nusing Microsoft.Extensions.Logging;\nusing Enyim.Caching.Memcached.Transcoders;\nusing RocksDbSharp;\nusing MessagePack;\nusing System.Text;\n\nnamespace Benchmark\n{\n\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);\n        }\n    }\n\n    public class BenchmarkConfig : ManualConfig\n    {\n        public BenchmarkConfig()\n        {\n            // run quickly:)\n            var baseConfig = Job.ShortRun.WithIterationCount(1).WithWarmupCount(1);\n\n            // Add(baseConfig.With(Runtime.Clr).With(Jit.RyuJit).With(Platform.X64));\n            Add(baseConfig.With(Runtime.Core).With(Jit.RyuJit).With(Platform.X64));\n            // Add(baseConfig.With(InProcessEmitToolchain.Instance));\n\n            Add(MarkdownExporter.GitHub);\n            Add(CsvExporter.Default);\n            Add(MemoryDiagnoser.Default);\n        }\n    }\n\n    [Config(typeof(BenchmarkConfig))]\n    public class SimpleRun\n    {\n        MemoryDatabase db;\n        SQLite_Test sqliteMemory;\n        SQLite_Test sqliteFile;\n\n        LiteDB_Test defaultLiteDb;\n        LiteDB_Test inmemoryLiteDb;\n\n        LiteDB_Test2 liteDb2;\n\n        MemcachedClient localMemcached;\n        Dictionary<int, TestDoc> dictionary;\n\n        RocksDb rocksDb;\n\n        const int QueryId = 741;\n\n        public SimpleRun()\n        {\n            var bin = new DatabaseBuilder().Append(MakeDoc(5000)).Build();\n            db = new MemoryDatabase(bin);\n\n            sqliteMemory = new SQLite_Test(5000, null, false, true);\n            sqliteMemory.Prepare(); sqliteMemory.Insert(); sqliteMemory.CreateIndex();\n            sqliteFile = new SQLite_Test(5000, null, false, false);\n            sqliteFile.Prepare(); sqliteFile.Insert(); sqliteFile.CreateIndex();\n\n\n            defaultLiteDb = new LiteDB_Test(5000, null, new LiteDB.FileOptions { Journal = true, FileMode = LiteDB.FileMode.Shared });\n            defaultLiteDb.Prepare(); defaultLiteDb.Insert(); defaultLiteDb.CreateIndex();\n            inmemoryLiteDb = new LiteDB_Test(5000);\n            inmemoryLiteDb.Prepare(); inmemoryLiteDb.Insert(); inmemoryLiteDb.CreateIndex();\n\n            liteDb2 = new LiteDB_Test2(5000);\n            liteDb2.Prepare(); liteDb2.Insert(); liteDb2.CreateIndex();\n\n            dictionary = new Dictionary<int, TestDoc>();\n            foreach (var item in MakeDoc(5000))\n            {\n                dictionary.Add(item.id, item);\n\n            }\n\n            {\n                var options = new DbOptions().SetCreateIfMissing(true);\n                var tempPath = Guid.NewGuid() + \".bin\";\n                rocksDb = RocksDb.Open(options, tempPath);\n                foreach (var item in MakeDoc(5000))\n                {\n                    rocksDb.Put(Encoding.UTF8.GetBytes(\"testdata.\" + item.id), MessagePackSerializer.Serialize(item));\n                }\n            }\n\n\n            var config = new MemcachedClientConfiguration(new LoggerDummy(), new Dummy());\n            localMemcached = new MemcachedClient(new LoggerDummy(), config);\n            foreach (var item in MakeDoc(5000))\n            {\n                localMemcached.Add(\"testdoc2.\" + item.id, item, 9999);\n            }\n\n        }\n\n        public IEnumerable<TestDoc> MakeDoc(int count)\n        {\n            foreach (var doc in Helper.GetDocs(count))\n            {\n                var v = new TestDoc\n                {\n                    id = (int)doc[\"_id\"],\n                    name = (string)doc[\"name\"],\n                    lorem = (string)doc[\"lorem\"]\n                };\n\n                yield return v;\n            }\n        }\n\n        [Benchmark(Baseline = true)]\n        public TestDoc MasterMemoryQuery()\n        {\n            return db.TestDocTable.FindByid(QueryId);\n        }\n\n        [Benchmark]\n        public TestDoc SQLiteInMemoryQuery()\n        {\n            using (var cmd = new SQLiteCommand(\"SELECT * FROM col WHERE id = @id\", sqliteMemory._db))\n            {\n                cmd.Parameters.Add(new SQLiteParameter(\"id\", DbType.Int32));\n                cmd.Parameters[\"id\"].Value = QueryId;\n\n                using (var r = cmd.ExecuteReader())\n                {\n                    r.Read();\n                    var id = r.GetInt32(0);\n                    var name = r.GetString(1);\n                    var lorem = r.GetString(2);\n                    return new TestDoc { id = 1, name = name, lorem = lorem };\n                }\n            }\n        }\n\n        [Benchmark]\n        public TestDoc SQLiteFileQuery()\n        {\n            using (var cmd = new SQLiteCommand(\"SELECT * FROM col WHERE id = @id\", sqliteFile._db))\n            {\n                cmd.Parameters.Add(new SQLiteParameter(\"id\", DbType.Int32));\n                cmd.Parameters[\"id\"].Value = QueryId;\n\n                using (var r = cmd.ExecuteReader())\n                {\n                    r.Read();\n                    var id = r.GetInt32(0);\n                    var name = r.GetString(1);\n                    var lorem = r.GetString(2);\n                    return new TestDoc { id = 1, name = name, lorem = lorem };\n                }\n            }\n        }\n\n\n\n        [Benchmark]\n        public BsonDocument LiteDbDefaultQuery()\n        {\n            return defaultLiteDb._db.FindOne(\"col\", LiteDB.Query.EQ(\"_id\", QueryId));\n        }\n\n        [Benchmark]\n        public BsonDocument LiteDbInMemoryQuery()\n        {\n            return inmemoryLiteDb._db.FindOne(\"col\", LiteDB.Query.EQ(\"_id\", QueryId));\n        }\n\n        [Benchmark]\n        public object LocalMemcachedQuery()\n        {\n            return localMemcached.Get(\"testdoc2.\" + QueryId);\n        }\n\n        //[Benchmark]\n        //public TestDoc DictionaryQuery()\n        //{\n        //    return dictionary.TryGetValue(QueryId, out var r) ? r : null;\n        //}\n\n        [Benchmark]\n        public TestDoc RocksDbQuery()\n        {\n            return MessagePackSerializer.Deserialize<TestDoc>(rocksDb.Get(Encoding.UTF8.GetBytes(\"testdata.\" + QueryId)));\n        }\n    }\n\n\n    public class SQLite_Test\n    {\n        private string _filename;\n        public SQLiteConnection _db;\n        private int _count;\n\n        public int Count { get { return _count; } }\n\n        public SQLite_Test(int count, string password, bool journal, bool memory = false)\n        {\n            _count = count;\n            _filename = \"sqlite-\" + Guid.NewGuid().ToString(\"n\") + \".db\";\n\n            if (memory)\n            {\n                var cs = \"Data Source=:memory:;New=True;\";\n                _db = new SQLiteConnection(cs);\n            }\n            else\n            {\n                var cs = \"Data Source=\" + _filename;\n                if (password != null) cs += \"; Password=\" + password;\n                if (journal == false) cs += \"; Journal Mode=Off\";\n                _db = new SQLiteConnection(cs);\n            }\n        }\n\n        public void Prepare()\n        {\n            _db.Open();\n\n            var table = new SQLiteCommand(\"CREATE TABLE col (id INTEGER NOT NULL PRIMARY KEY, name TEXT, lorem TEXT)\", _db);\n            table.ExecuteNonQuery();\n\n            var table2 = new SQLiteCommand(\"CREATE TABLE col_bulk (id INTEGER NOT NULL PRIMARY KEY, name TEXT, lorem TEXT)\", _db);\n            table2.ExecuteNonQuery();\n        }\n\n        public void Insert()\n        {\n            // standard insert is slow, mod same as Bulk\n            using (var trans = _db.BeginTransaction())\n            {\n                var cmd = new SQLiteCommand(\"INSERT INTO col (id, name, lorem) VALUES (@id, @name, @lorem)\", _db);\n\n                cmd.Parameters.Add(new SQLiteParameter(\"id\", DbType.Int32));\n                cmd.Parameters.Add(new SQLiteParameter(\"name\", DbType.String));\n                cmd.Parameters.Add(new SQLiteParameter(\"lorem\", DbType.String));\n\n                foreach (var doc in Helper.GetDocs(_count))\n                {\n                    cmd.Parameters[\"id\"].Value = (int)doc[\"_id\"];\n                    cmd.Parameters[\"name\"].Value = (string)doc[\"name\"];\n                    cmd.Parameters[\"lorem\"].Value = (string)doc[\"lorem\"];\n\n                    cmd.ExecuteNonQuery();\n                }\n\n                trans.Commit();\n            }\n        }\n\n        public void CreateIndex()\n        {\n            var cmd = new SQLiteCommand(\"CREATE INDEX idx1 ON col (name)\", _db);\n\n            cmd.ExecuteNonQuery();\n        }\n\n        public void Query()\n        {\n            var cmd = new SQLiteCommand(\"SELECT * FROM col WHERE id = @id\", _db);\n\n            cmd.Parameters.Add(new SQLiteParameter(\"id\", DbType.Int32));\n\n            for (var i = 0; i < _count; i++)\n            {\n                cmd.Parameters[\"id\"].Value = i;\n\n                var r = cmd.ExecuteReader();\n\n                r.Read();\n\n                var name = r.GetString(1);\n                var lorem = r.GetString(2);\n\n                r.Close();\n            }\n        }\n\n\n        public void Dispose()\n        {\n            _db.Dispose();\n        }\n    }\n\n\n    public class LiteDB_Test\n    {\n        private string _filename;\n        public LiteEngine _db;\n        private int _count;\n\n        public int Count { get { return _count; } }\n\n        public LiteDB_Test(int count, string password, LiteDB.FileOptions options)\n        {\n            _count = count;\n            _filename = \"dblite-\" + Guid.NewGuid().ToString(\"n\") + \".db\";\n\n            var disk = new FileDiskService(_filename, options);\n\n            _db = new LiteEngine(disk, password);\n        }\n\n        public LiteDB_Test(int count)\n        {\n            _count = count;\n            _filename = \"dblite-\" + Guid.NewGuid().ToString(\"n\") + \".db\";\n\n            var ms = new MemoryStream();\n            var disk = new LiteDB.StreamDiskService(ms);\n            //var disk = new FileDiskService(_filename, options);\n\n            _db = new LiteEngine(disk);\n        }\n\n        public void Prepare()\n        {\n        }\n\n        public void Insert()\n        {\n            foreach (var doc in Helper.GetDocs(_count))\n            {\n                _db.Insert(\"col\", doc);\n            }\n        }\n\n        public void Bulk()\n        {\n            _db.Insert(\"col_bulk\", Helper.GetDocs(_count));\n        }\n\n        public void Update()\n        {\n            foreach (var doc in Helper.GetDocs(_count))\n            {\n                _db.Update(\"col\", doc);\n            }\n        }\n\n        public void CreateIndex()\n        {\n            _db.EnsureIndex(\"col\", \"name\", false);\n        }\n\n        public void Query()\n        {\n            for (var i = 0; i < _count; i++)\n            {\n                _db.Find(\"col\", LiteDB.Query.EQ(\"_id\", i)).Single();\n            }\n        }\n\n        public void Delete()\n        {\n            _db.Delete(\"col\", LiteDB.Query.All());\n        }\n\n        public void Drop()\n        {\n            _db.DropCollection(\"col_bulk\");\n        }\n\n        public void Dispose()\n        {\n            _db.Dispose();\n            File.Delete(_filename);\n        }\n    }\n\n\n    public class LiteDB_Test2\n    {\n        private string _filename;\n        public LiteDatabase _db;\n        private int _count;\n        public LiteCollection<TestDoc> _collection;\n\n        public int Count { get { return _count; } }\n\n        public LiteDB_Test2(int count, string password, LiteDB.FileOptions options)\n        {\n            _count = count;\n            _filename = \"dblite-\" + Guid.NewGuid().ToString(\"n\") + \".db\";\n\n            var disk = new FileDiskService(_filename, options);\n\n            _db = new LiteDatabase(disk);\n\n            _collection = _db.GetCollection<TestDoc>();\n        }\n\n        public LiteDB_Test2(int count)\n        {\n            _count = count;\n            _filename = \"dblite-\" + Guid.NewGuid().ToString(\"n\") + \".db\";\n\n            var ms = new MemoryStream();\n            var disk = new LiteDB.StreamDiskService(ms);\n            //var disk = new FileDiskService(_filename, options);\n\n            _db = new LiteDatabase(disk);\n\n            _collection = _db.GetCollection<TestDoc>();\n        }\n\n        public void Prepare()\n        {\n        }\n\n        public void Insert()\n        {\n            foreach (var doc in Helper.GetDocs(_count))\n            {\n                _collection.Insert(new TestDoc\n                {\n                    //  id = (int)doc[\"_id\"],\n                    name = (string)doc[\"name\"],\n                    lorem = (string)doc[\"lorem\"]\n                });\n            }\n        }\n\n        public void CreateIndex()\n        {\n            //_collection.EnsureIndex(x => x.id, true);\n        }\n    }\n\n\n    class Dummy : IOptions<MemcachedClientOptions>\n    {\n        public MemcachedClientOptions Value => new MemcachedClientOptions\n        {\n            Servers = new List<Server> { new Server { Address = \"127.0.0.1\", Port = 11211 } },\n            Protocol = MemcachedProtocol.Binary,\n            // Transcoder = new BinaryFormatterTranscoder()\n        };\n    }\n\n    class LoggerDummy : ILoggerFactory\n    {\n        public void AddProvider(ILoggerProvider provider)\n        {\n        }\n\n        public ILogger CreateLogger(string categoryName)\n        {\n            return new NullLogger();\n        }\n\n        public void Dispose()\n        {\n\n        }\n        class NullLogger : ILogger\n        {\n            public IDisposable BeginScope<TState>(TState state)\n            {\n                return new EmptyDisposable();\n            }\n\n            public bool IsEnabled(LogLevel logLevel)\n            {\n                return false;\n            }\n\n            public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)\n            {\n            }\n\n            class EmptyDisposable : IDisposable\n            {\n                public void Dispose()\n                {\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "sandbox/Benchmark/Utils/Helper.cs",
    "content": "﻿#pragma warning disable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing MessagePack;\nusing LiteDB;\nusing MasterMemory;\n\nnamespace TestPerfLiteDB\n{\n    [MemoryTable(\"TestDoc\"), MessagePackObject(true)]\n    public class TestDoc\n    {\n        [PrimaryKey]\n        public int id { get; set; }\n        public string name { get; set; }\n        public string lorem { get; set; }\n\n        public TestDoc()\n        {\n\n        }\n\n        public TestDoc(int id, string name, string lorem)\n        {\n            this.id = id;\n            this.name = name;\n            this.lorem = lorem;\n        }\n    }\n\n    public static class Helper\n    {\n        public static IEnumerable<BsonDocument> GetDocs(int count)\n        {\n            for (var i = 0; i < count; i++)\n            {\n                yield return new BsonDocument\n                {\n                    { \"_id\", i },\n                    { \"name\", Guid.NewGuid().ToString() },\n                    { \"lorem\", LoremIpsum(3, 5, 2, 3, 3) }\n                };\n            }\n        }\n\n        public static string LoremIpsum(int minWords, int maxWords,\n            int minSentences, int maxSentences,\n            int numParagraphs)\n        {\n            var words = new[] { \"lorem\", \"ipsum\", \"dolor\", \"sit\", \"amet\", \"consectetuer\",\n                \"adipiscing\", \"elit\", \"sed\", \"diam\", \"nonummy\", \"nibh\", \"euismod\",\n                \"tincidunt\", \"ut\", \"laoreet\", \"dolore\", \"magna\", \"aliquam\", \"erat\" };\n\n            var rand = new Random(DateTime.Now.Millisecond);\n            var numSentences = rand.Next(maxSentences - minSentences) + minSentences + 1;\n            var numWords = rand.Next(maxWords - minWords) + minWords + 1;\n\n            var result = new StringBuilder();\n\n            for (int p = 0; p < numParagraphs; p++)\n            {\n                for (int s = 0; s < numSentences; s++)\n                {\n                    for (int w = 0; w < numWords; w++)\n                    {\n                        if (w > 0) { result.Append(\" \"); }\n                        result.Append(words[rand.Next(words.Length)]);\n                    }\n                    result.Append(\". \");\n                }\n                result.AppendLine();\n            }\n\n            return result.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "sandbox/ConsoleApp/ConsoleApp.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<OutputType>Exe</OutputType>\n\t\t<TargetFramework>net9.0</TargetFramework>\n\t</PropertyGroup>\n\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|AnyCPU'\">\n\t\t<DefineConstants>$(DefineConstants)TRACE;</DefineConstants>\n\t</PropertyGroup>\n\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|AnyCPU'\">\n\t\t<DefineConstants>$(DefineConstants)TRACE;</DefineConstants>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<ProjectReference Include=\"..\\..\\src\\MasterMemory.Annotations\\MasterMemory.Annotations.csproj\" />\n\t\t<ProjectReference Include=\"..\\..\\src\\MasterMemory\\MasterMemory.csproj\" />\n\t\t<ProjectReference Include=\"..\\..\\src\\MasterMemory.SourceGenerator\\MasterMemory.SourceGenerator.csproj\">\n\t\t\t<OutputItemType>Analyzer</OutputItemType>\n\t\t\t<ReferenceOutputAssembly>false</ReferenceOutputAssembly>\n\t\t</ProjectReference>\n\t</ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "sandbox/ConsoleApp/Program.cs",
    "content": "﻿#pragma warning disable CS8618\n#pragma warning disable CS8602\n#pragma warning disable CS8603\n\nusing MasterMemory;\nusing MessagePack;\nusing System;\nusing System.Buffers;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.IO;\nusing System.Linq;\nusing System.Runtime.CompilerServices;\nusing System.Text;\n\n\n[assembly: MasterMemoryGeneratorOptions(\n    Namespace = \"ConsoleApp\" // ,\n    // IsReturnNullIfKeyNotFound = true,\n    // PrefixClassName = \"Foo\"\n)]\n\npublic class FooItem\n{\n    public bool TryFindByItemId(int key, out Item result)\n    {\n        result = default!;\n        return true;\n    }\n}\n\n[MemoryTable(\"quest_master\"), MessagePackObject(true)]\npublic class Quest : IValidatable<Quest>\n{\n    [PrimaryKey]\n    public int Id { get; set; }\n    public string Name { get; set; }\n    public int RewardId { get; set; }\n    public int Cost { get; set; }\n    public MyEnum MyProperty { get; set; }\n\n    void IValidatable<Quest>.Validate(IValidator<Quest> validator)\n    {\n        var items = validator.GetReferenceSet<Item>();\n\n        if (this.RewardId > 0)\n        {\n            items.Exists(x => x.RewardId, x => x.ItemId);\n        }\n\n        validator.Validate(x => x.Cost >= 10);\n        validator.Validate(x => x.Cost <= 20);\n\n        if (validator.CallOnce())\n        {\n            var quests = validator.GetTableSet();\n            quests.Where(x => x.RewardId != 0).Unique(x => x.RewardId);\n        }\n    }\n\n    public enum MyEnum\n    {\n        A, B, C\n    }\n}\n\n[MemoryTable(\"item\"), MessagePackObject(true)]\npublic class Item\n{\n    [PrimaryKey]\n    public int ItemId { get; set; }\n}\n\nnamespace ConsoleApp.Tables\n{\n    public sealed partial class MonsterTable\n    {\n        /* readonly */\n        int maxHp;\n\n        partial void OnAfterConstruct()\n        {\n            maxHp = All.Select(x => x.MaxHp).Max();\n        }\n    }\n}\n\nnamespace ConsoleApp\n{\n    [MemoryTable(\"monster\"), MessagePackObject(true)]\n    public partial class Monster\n    {\n        [PrimaryKey]\n        public int MonsterId { get; private set; }\n        public string Name { get; private set; }\n        public int MaxHp { get; private set; }\n\n        public Monster(int MonsterId, string Name, int MaxHp)\n        {\n            this.MonsterId = MonsterId;\n            this.Name = Name;\n            this.MaxHp = MaxHp;\n        }\n    }\n\n    [MemoryTable(\"enumkeytable\"), MessagePackObject(true)]\n    public class EnumKeyTable\n    {\n        [PrimaryKey]\n        public Gender Gender { get; set; }\n    }\n\n    public enum Gender\n    {\n        Male, Female\n    }\n\n    [MemoryTable(\"person\"), MessagePackObject(true)]\n    public class Person\n    {\n        [PrimaryKey(keyOrder: 1)]\n        public int PersonId { get; set; }\n        [SecondaryKey(0), NonUnique]\n        [SecondaryKey(2, keyOrder: 1), NonUnique]\n        public int Age { get; set; }\n        [SecondaryKey(1), NonUnique]\n        [SecondaryKey(2, keyOrder: 0), NonUnique]\n        public Gender Gender { get; set; }\n        public string Name { get; set; }\n\n        public Person()\n        {\n        }\n\n        public Person(int PersonId, int Age, Gender Gender, string Name)\n        {\n            this.PersonId = PersonId;\n            this.Age = Age;\n            this.Gender = Gender;\n            this.Name = Name;\n        }\n\n        public override string ToString()\n        {\n            return $\"{PersonId} {Age} {Gender} {Name}\";\n        }\n    }\n\n\n\n\n\n    class ByteBufferWriter : IBufferWriter<byte>\n    {\n        byte[] buffer;\n        int index;\n\n        public int CurrentOffset => index;\n        public ReadOnlySpan<byte> WrittenSpan => buffer.AsSpan(0, index);\n        public ReadOnlyMemory<byte> WrittenMemory => new ReadOnlyMemory<byte>(buffer, 0, index);\n\n        public ByteBufferWriter()\n        {\n            buffer = new byte[1024];\n            index = 0;\n        }\n\n        public void Advance(int count)\n        {\n            index += count;\n        }\n\n        public Memory<byte> GetMemory(int sizeHint = 0)\n        {\n        AGAIN:\n            var nextSize = index + sizeHint;\n            if (buffer.Length < nextSize)\n            {\n                Array.Resize(ref buffer, Math.Max(buffer.Length * 2, nextSize));\n            }\n\n            if (sizeHint == 0)\n            {\n                var result = new Memory<byte>(buffer, index, buffer.Length - index);\n                if (result.Length == 0)\n                {\n                    sizeHint = 1024;\n                    goto AGAIN;\n                }\n                return result;\n            }\n            else\n            {\n                return new Memory<byte>(buffer, index, sizeHint);\n            }\n        }\n\n        public Span<byte> GetSpan(int sizeHint = 0)\n        {\n            return GetMemory(sizeHint).Span;\n        }\n    }\n\n    [MemoryTable(nameof(Test1))]\n    public class Test1\n    {\n        [PrimaryKey]\n        public int Id { get; set; }\n    }\n\n    [MessagePackObject(false)]\n    [MemoryTable(nameof(Test2))]\n    public class Test2\n    {\n        [PrimaryKey]\n        [Key(0)]\n        public int Id { get; set; }\n    }\n\n\n\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            var csv = @\"monster_id,name,max_hp\n    1,foo,100\n    2,bar,200\";\n            var fileName = \"monster\";\n\n            var builder = new DatabaseBuilder();\n\n            var meta = MemoryDatabase.GetMetaDatabase();\n            var table = meta.GetTableInfo(fileName);\n\n            var tableData = new List<object>();\n\n            using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(csv)))\n            using (var sr = new StreamReader(ms, Encoding.UTF8))\n            using (var reader = new TinyCsvReader(sr))\n            {\n                while ((reader.ReadValuesWithHeader() is Dictionary<string, string> values))\n                {\n                    // create data without call constructor\n                    var data = RuntimeHelpers.GetUninitializedObject(table.DataType);\n\n                    foreach (var prop in table.Properties)\n                    {\n                        if (values.TryGetValue(prop.NameSnakeCase, out var rawValue))\n                        {\n                            var value = ParseValue(prop.PropertyInfo.PropertyType, rawValue);\n                            if (prop.PropertyInfo.SetMethod == null)\n                            {\n                                throw new Exception(\"Target property does not exists set method. If you use {get;}, please change to { get; private set; }, Type:\" + prop.PropertyInfo.DeclaringType + \" Prop:\" + prop.PropertyInfo.Name);\n                            }\n                            prop.PropertyInfo.SetValue(data, value);\n                        }\n                        else\n                        {\n                            throw new KeyNotFoundException($\"Not found \\\"{prop.NameSnakeCase}\\\" in \\\"{fileName}.csv\\\" header.\");\n                        }\n                    }\n\n                    tableData.Add(data);\n                }\n            }\n\n            // add dynamic collection.\n            builder.AppendDynamic(table.DataType, tableData);\n\n            var bin = builder.Build();\n            var database = new MemoryDatabase(bin, maxDegreeOfParallelism: Environment.ProcessorCount);\n        }\n\n        static object ParseValue(Type type, string rawValue)\n        {\n            if (type == typeof(string)) return rawValue;\n\n            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))\n            {\n                if (string.IsNullOrWhiteSpace(rawValue)) return null;\n                return ParseValue(type.GenericTypeArguments[0], rawValue);\n            }\n\n            if (type.IsEnum)\n            {\n                var value = Enum.Parse(type, rawValue);\n                return value;\n            }\n\n            switch (Type.GetTypeCode(type))\n            {\n                case TypeCode.Boolean:\n                    // True/False or 0,1\n                    if (int.TryParse(rawValue, out var intBool))\n                    {\n                        return Convert.ToBoolean(intBool);\n                    }\n                    return Boolean.Parse(rawValue);\n                case TypeCode.Char:\n                    return Char.Parse(rawValue);\n                case TypeCode.SByte:\n                    return SByte.Parse(rawValue, CultureInfo.InvariantCulture);\n                case TypeCode.Byte:\n                    return Byte.Parse(rawValue, CultureInfo.InvariantCulture);\n                case TypeCode.Int16:\n                    return Int16.Parse(rawValue, CultureInfo.InvariantCulture);\n                case TypeCode.UInt16:\n                    return UInt16.Parse(rawValue, CultureInfo.InvariantCulture);\n                case TypeCode.Int32:\n                    return Int32.Parse(rawValue, CultureInfo.InvariantCulture);\n                case TypeCode.UInt32:\n                    return UInt32.Parse(rawValue, CultureInfo.InvariantCulture);\n                case TypeCode.Int64:\n                    return Int64.Parse(rawValue, CultureInfo.InvariantCulture);\n                case TypeCode.UInt64:\n                    return UInt64.Parse(rawValue, CultureInfo.InvariantCulture);\n                case TypeCode.Single:\n                    return Single.Parse(rawValue, CultureInfo.InvariantCulture);\n                case TypeCode.Double:\n                    return Double.Parse(rawValue, CultureInfo.InvariantCulture);\n                case TypeCode.Decimal:\n                    return Decimal.Parse(rawValue, CultureInfo.InvariantCulture);\n                case TypeCode.DateTime:\n                    return DateTime.Parse(rawValue, CultureInfo.InvariantCulture);\n                default:\n                    if (type == typeof(DateTimeOffset))\n                    {\n                        return DateTimeOffset.Parse(rawValue, CultureInfo.InvariantCulture);\n                    }\n                    else if (type == typeof(TimeSpan))\n                    {\n                        return TimeSpan.Parse(rawValue, CultureInfo.InvariantCulture);\n                    }\n                    else if (type == typeof(Guid))\n                    {\n                        return Guid.Parse(rawValue);\n                    }\n\n                    // or other your custom parsing.\n                    throw new NotSupportedException();\n            }\n        }\n\n        // Non string escape, tiny reader with header.\n        public class TinyCsvReader : IDisposable\n        {\n            static char[] trim = new[] { ' ', '\\t' };\n\n            readonly StreamReader reader;\n            public IReadOnlyList<string> Header { get; private set; }\n\n            public TinyCsvReader(StreamReader reader)\n            {\n                this.reader = reader;\n                {\n                    var line = reader.ReadLine();\n                    if (line == null) throw new InvalidOperationException(\"Header is null.\");\n\n                    var index = 0;\n                    var header = new List<string>();\n                    while (index < line.Length)\n                    {\n                        var s = GetValue(line, ref index);\n                        if (s.Length == 0) break;\n                        header.Add(s);\n                    }\n                    this.Header = header;\n                }\n            }\n\n            string GetValue(string line, ref int i)\n            {\n                var temp = new char[line.Length - i];\n                var j = 0;\n                for (; i < line.Length; i++)\n                {\n                    if (line[i] == ',')\n                    {\n                        i += 1;\n                        break;\n                    }\n                    temp[j++] = line[i];\n                }\n\n                return new string(temp, 0, j).Trim(trim);\n            }\n\n            public string[] ReadValues()\n            {\n                var line = reader.ReadLine();\n                if (line == null) return null;\n                if (string.IsNullOrWhiteSpace(line)) return null;\n\n                var values = new string[Header.Count];\n                var lineIndex = 0;\n                for (int i = 0; i < values.Length; i++)\n                {\n                    var s = GetValue(line, ref lineIndex);\n                    values[i] = s;\n                }\n                return values;\n            }\n\n            public Dictionary<string, string> ReadValuesWithHeader()\n            {\n                var values = ReadValues();\n                if (values == null) return null;\n\n                var dict = new Dictionary<string, string>();\n                for (int i = 0; i < values.Length; i++)\n                {\n                    dict.Add(Header[i], values[i]);\n                }\n\n                return dict;\n            }\n\n            public void Dispose()\n            {\n                reader.Dispose();\n            }\n        }\n    }\n\n\n}\n\n\n"
  },
  {
    "path": "sandbox/GeneratorSandbox/GeneratorSandbox.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<OutputType>Exe</OutputType>\n\t\t<TargetFramework>net9.0</TargetFramework>\n\t\t<ImplicitUsings>enable</ImplicitUsings>\n\t\t<Nullable>enable</Nullable>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<PackageReference Include=\"MessagePack\" Version=\"3.1.3\" />\n\t</ItemGroup>\n\n\t<!-- for testing... -->\n\t<!--<PropertyGroup>\n\t\t<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>\n\t\t<CompilerGeneratedFilesOutputPath>$(ProjectDir).\\Generated</CompilerGeneratedFilesOutputPath>\n\t</PropertyGroup>-->\n\n\t<ItemGroup>\n\t\t<ProjectReference Include=\"..\\..\\src\\MasterMemory\\MasterMemory.csproj\" />\n\t\t<ProjectReference Include=\"..\\..\\src\\MasterMemory.Annotations\\MasterMemory.Annotations.csproj\" />\n\t\t<ProjectReference Include=\"..\\..\\src\\MasterMemory.SourceGenerator\\MasterMemory.SourceGenerator.csproj\">\n\t\t\t<OutputItemType>Analyzer</OutputItemType>\n\t\t\t<ReferenceOutputAssembly>false</ReferenceOutputAssembly>\n\t\t</ProjectReference>\n\t</ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "sandbox/GeneratorSandbox/Program.cs",
    "content": "﻿using MasterMemory;\nusing MessagePack;\nusing GeneratorSandbox;\nusing System.Runtime.CompilerServices;\n\n//[assembly: MasterMemoryGeneratorOptions(\n//    Namespace = \"Z\",\n//    IsReturnNullIfKeyNotFound = true,\n//    PrefixClassName = \"\")]\n\n// to create database, use DatabaseBuilder and Append method.\nvar builder = new DatabaseBuilder();\nbuilder.Append(new Person[]\n{\n    new (){ PersonId = 0, Age = 13, Gender = Gender.Male,   Name = \"Dana Terry\" },\n    new (){ PersonId = 1, Age = 17, Gender = Gender.Male,   Name = \"Kirk Obrien\" },\n    new (){ PersonId = 2, Age = 31, Gender = Gender.Male,   Name = \"Wm Banks\" },\n    new (){ PersonId = 3, Age = 44, Gender = Gender.Male,   Name = \"Karl Benson\" },\n    new (){ PersonId = 4, Age = 23, Gender = Gender.Male,   Name = \"Jared Holland\" },\n    new (){ PersonId = 5, Age = 27, Gender = Gender.Female, Name = \"Jeanne Phelps\" },\n    new (){ PersonId = 6, Age = 25, Gender = Gender.Female, Name = \"Willie Rose\" },\n    new (){ PersonId = 7, Age = 11, Gender = Gender.Female, Name = \"Shari Gutierrez\" },\n    new (){ PersonId = 8, Age = 63, Gender = Gender.Female, Name = \"Lori Wilson\" },\n    new (){ PersonId = 9, Age = 34, Gender = Gender.Female, Name = \"Lena Ramsey\" },\n});\n\n// build database binary(you can also use `WriteToStream` for save to file).\nbyte[] data = builder.Build();\n\n\nvar db = new MemoryDatabase(data);\n\n// .PersonTable.FindByPersonId is fully typed by code-generation.\nPerson person = db.PersonTable.FindByPersonId(5);\n\n// Multiple key is also typed(***And * **), Return value is multiple if key is marked with `NonUnique`.\nRangeView<Person> result = db.PersonTable.FindByGenderAndAge((Gender.Female, 23));\n\n// Get nearest value(choose lower(default) or higher).\nRangeView<Person> age1 = db.PersonTable.FindClosestByAge(31);\n\n// Get range(min-max inclusive).\nRangeView<Person> age2 = db.PersonTable.FindRangeByAge(20, 29);\n\n\npublic enum Gender\n{\n    Male, Female, Unknown\n}\n\n\n\n// table definition marked by MemoryTableAttribute.\n// database-table must be serializable by MessagePack-CSsharp\n[MemoryTable(\"person\"), MessagePackObject(true)]\npublic record Person\n{\n    // index definition by attributes.\n    [PrimaryKey]\n    public required int PersonId { get; init; }\n\n    // secondary index can add multiple(discriminated by index-number).\n    [SecondaryKey(0), NonUnique]\n    [SecondaryKey(1, keyOrder: 1), NonUnique]\n    public required int Age { get; init; }\n\n    [SecondaryKey(2), NonUnique]\n    [SecondaryKey(1, keyOrder: 0), NonUnique]\n    public required Gender Gender { get; init; }\n\n    public required string Name { get; init; }\n}"
  },
  {
    "path": "sandbox/PerfTest2/Engines/Dictionary_Test.cs",
    "content": "﻿#pragma warning disable\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Collections.Immutable;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing TestPerfLiteDB;\n\nnamespace TestPerfLiteDB\n{\n    public class Dictionary_Test : ITest\n    {\n        private string _filename;\n        private int _count;\n\n        Dictionary<int, TestDoc> dict;\n\n        public int Count { get { return _count; } }\n        public int FileLength { get { return (int)new FileInfo(_filename).Length; } }\n\n        public Dictionary_Test(int count)\n        {\n            _count = count;\n            _filename = \"dict-\" + Guid.NewGuid().ToString(\"n\") + \".db\";\n            dict = new Dictionary<int, TestPerfLiteDB.TestDoc>();\n        }\n\n        public void Insert()\n        {\n            foreach (var doc in Helper.GetDocs(_count))\n            {\n                var v = new TestDoc\n                {\n                    id = doc[\"_id\"].AsInt32,\n                    name = doc[\"name\"].AsString,\n                    lorem = doc[\"lorem\"].AsString\n                };\n\n                dict.Add(v.id, v);\n            }\n        }\n\n        public void Bulk()\n        {\n\n        }\n\n        public void CreateIndex()\n        {\n\n        }\n\n        public void Dispose()\n        {\n\n        }\n\n        public void Prepare()\n        {\n\n        }\n\n        public void Query()\n        {\n            for (var i = 0; i < _count; i++)\n            {\n                TestDoc d;\n                dict.TryGetValue(i, out d);\n            }\n        }\n\n        public void Update()\n        {\n\n        }\n    }\n\n    public class ConcurrentDictionary_Test : ITest\n    {\n        private string _filename;\n        private int _count;\n\n        ConcurrentDictionary<int, TestDoc> dict;\n\n        public int Count { get { return _count; } }\n        public int FileLength { get { return (int)new FileInfo(_filename).Length; } }\n\n        public ConcurrentDictionary_Test(int count)\n        {\n            _count = count;\n            _filename = \"concurrentdict-\" + Guid.NewGuid().ToString(\"n\") + \".db\";\n            dict = new ConcurrentDictionary<int, TestPerfLiteDB.TestDoc>();\n        }\n\n        public void Insert()\n        {\n            foreach (var doc in Helper.GetDocs(_count))\n            {\n                var v = new TestDoc\n                {\n                    id = doc[\"_id\"].AsInt32,\n                    name = doc[\"name\"].AsString,\n                    lorem = doc[\"lorem\"].AsString\n                };\n\n                dict.TryAdd(v.id, v);\n            }\n        }\n\n        public void Bulk()\n        {\n\n        }\n\n        public void CreateIndex()\n        {\n\n        }\n\n        public void Dispose()\n        {\n\n        }\n\n        public void Prepare()\n        {\n\n        }\n\n        public void Query()\n        {\n            for (var i = 0; i < _count; i++)\n            {\n                TestDoc d;\n                dict.TryGetValue(i, out d);\n            }\n        }\n\n        public void Update()\n        {\n\n        }\n    }\n\n    public class ImmutableDictionary_Test : ITest\n    {\n        private string _filename;\n        private int _count;\n\n        ImmutableDictionary<int, TestDoc> dict;\n\n        public int Count { get { return _count; } }\n        public int FileLength { get { return (int)new FileInfo(_filename).Length; } }\n\n        public ImmutableDictionary_Test(int count)\n        {\n            _count = count;\n            _filename = \"immutabledict-\" + Guid.NewGuid().ToString(\"n\") + \".db\";\n            //dict = new ImmutableDictionary<int, TestPerfLiteDB.TestDoc>();\n        }\n\n        public void Insert()\n        {\n            var builder = ImmutableDictionary.CreateBuilder<int, TestDoc>();\n            foreach (var doc in Helper.GetDocs(_count))\n            {\n                var v = new TestDoc\n                {\n                    id = doc[\"_id\"].AsInt32,\n                    name = doc[\"name\"].AsString,\n                    lorem = doc[\"lorem\"].AsString\n                };\n\n                builder.Add(v.id, v);\n            }\n\n            dict = builder.ToImmutableDictionary();\n        }\n\n        public void Bulk()\n        {\n\n        }\n\n        public void CreateIndex()\n        {\n\n        }\n\n        public void Dispose()\n        {\n\n        }\n\n        public void Prepare()\n        {\n\n        }\n\n        public void Query()\n        {\n            for (var i = 0; i < _count; i++)\n            {\n                TestDoc d;\n                dict.TryGetValue(i, out d);\n            }\n        }\n\n        public void Update()\n        {\n\n        }\n    }\n}\n"
  },
  {
    "path": "sandbox/PerfTest2/Engines/ITest.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.SQLite;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing LiteDB;\n\nnamespace TestPerfLiteDB\n{\n    public interface ITest : IDisposable\n    {\n        int Count { get; }\n        int FileLength { get; }\n\n        void Prepare();\n        void Insert();\n        void Bulk();\n        void Update();\n        void CreateIndex();\n        void Query();\n        //void Delete();\n        //void Drop();\n    }\n}\n"
  },
  {
    "path": "sandbox/PerfTest2/Engines/LiteDB_Test.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.SQLite;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing LiteDB;\n\nnamespace TestPerfLiteDB\n{\n    public class LiteDB_Test : ITest\n    {\n        private string _filename;\n        private LiteEngine _db;\n        private int _count;\n\n        public int Count { get { return _count; } }\n        public int FileLength { get { return (int)new FileInfo(_filename).Length; } }\n\n        public LiteDB_Test(int count, string password, LiteDB.FileOptions options)\n        {\n            _count = count;\n            _filename = \"dblite-\" + Guid.NewGuid().ToString(\"n\") + \".db\";\n\n            var disk = new FileDiskService(_filename, options);\n\n            _db = new LiteEngine(disk, password);\n        }\n\n        public LiteDB_Test(int count)\n        {\n            _count = count;\n            _filename = \"dblite-\" + Guid.NewGuid().ToString(\"n\") + \".db\";\n\n            var ms = new MemoryStream();\n            var disk = new LiteDB.StreamDiskService(ms);\n            //var disk = new FileDiskService(_filename, options);\n\n            _db = new LiteEngine(disk);\n        }\n\n        public void Prepare()\n        {\n        }\n\n        public void Insert()\n        {\n            foreach (var doc in Helper.GetDocs(_count))\n            {\n                _db.Insert(\"col\", doc);\n            }\n        }\n\n        public void Bulk()\n        {\n            _db.Insert(\"col_bulk\", Helper.GetDocs(_count));\n        }\n\n        public void Update()\n        {\n            foreach (var doc in Helper.GetDocs(_count))\n            {\n                _db.Update(\"col\", doc);\n            }\n        }\n\n        public void CreateIndex()\n        {\n            _db.EnsureIndex(\"col\", \"name\", false);\n        }\n\n        public void Query()\n        {\n            for (var i = 0; i < _count; i++)\n            {\n                _db.Find(\"col\", LiteDB.Query.EQ(\"_id\", i)).Single();\n            }\n        }\n\n        public void Delete()\n        {\n            _db.Delete(\"col\", LiteDB.Query.All());\n        }\n\n        public void Drop()\n        {\n            _db.DropCollection(\"col_bulk\");\n        }\n\n        public void Dispose()\n        {\n            _db.Dispose();\n            File.Delete(_filename);\n        }\n    }\n}\n"
  },
  {
    "path": "sandbox/PerfTest2/Engines/MasterMemory_Test.cs",
    "content": "﻿#pragma warning disable\nusing MasterMemory;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace TestPerfLiteDB\n{\n    public class MasterMemory_Test : ITest\n    {\n        private string _filename;\n        private int _count;\n\n        MemoryDatabase database;\n\n        public int Count { get { return _count; } }\n        public int FileLength { get { return (int)new FileInfo(_filename).Length; } }\n\n        public MasterMemory_Test(int count)\n        {\n            _count = count;\n            _filename = \"mastermemorydatabase-\" + Guid.NewGuid().ToString(\"n\") + \".db\";\n\n        }\n\n        public IEnumerable<TestDoc> MakeDoc()\n        {\n            foreach (var doc in Helper.GetDocs(_count))\n            {\n                var v = new TestDoc\n                {\n                    id = doc[\"_id\"].AsInt32,\n                    name = doc[\"name\"].AsString,\n                    lorem = doc[\"lorem\"].AsString\n                };\n\n                yield return v;\n            }\n        }\n\n        public void Insert()\n        {\n            var builder = new DatabaseBuilder();\n            builder.Append(MakeDoc());\n            var saved = builder.Build();\n            File.WriteAllBytes(_filename, saved);\n            database = new MemoryDatabase(saved);\n        }\n\n        public void Bulk()\n        {\n\n        }\n\n        public void CreateIndex()\n        {\n\n        }\n\n        public void Dispose()\n        {\n\n        }\n\n        public void Prepare()\n        {\n\n        }\n\n        public void Query()\n        {\n            for (var i = 0; i < _count; i++)\n            {\n                //TestDoc d;\n                database.TestDocTable.FindByid(i);\n            }\n        }\n\n        public void Update()\n        {\n\n        }\n    }\n}\n"
  },
  {
    "path": "sandbox/PerfTest2/Engines/RavenDB_Test.cs",
    "content": "﻿//using Raven.Client;\n//using Raven.Client.Embedded;\n//using Raven.Client.Indexes;\n//using System;\n//using System.Collections.Generic;\n//using System.IO;\n//using System.Linq;\n//using System.Text;\n//using System.Threading.Tasks;\n\n//namespace TestPerfLiteDB\n//{\n//    public class TestDocCreation : AbstractIndexCreationTask<TestDoc>\n//    {\n//        public TestDocCreation()\n//        {\n//            Map = xs => xs.Select(x => new { x.id });\n//        }\n//    }\n\n//    public class RavenDB_Test : ITest\n//    {\n//        private string _filename;\n//        private int _count;\n//        private bool isinmemory;\n\n//        IDocumentStore store;\n//        public int Count { get { return _count; } }\n//        public int FileLength { get { return (int)new FileInfo(_filename).Length; } }\n\n//        public RavenDB_Test(int count, bool isinmemory)\n//        {\n//            _count = count;\n//            _filename = \"ravendb-\" + Guid.NewGuid().ToString(\"n\") + \".db\";\n//            this.isinmemory = isinmemory;\n//        }\n\n//        public void Bulk()\n//        {\n//            if (isinmemory)\n//            {\n\n\n//            }\n\n//            //using (var store = new EmbeddableDocumentStore { RunInMemory = true }.Initialize())\n//            //{\n//            //    using (var bulk = store.BulkInsert())\n//            //    {\n//            //        bulk.Store(new MyClass { Id = 9999, MyProperty = 1000 });\n//            //    }\n\n//            //    var session = store.OpenSession();\n\n//            //    store.ExecuteIndex(new MyClassIndex());\n\n//            //    var huga = session.Load<MyClass>(\"MyClasses/9999\");\n//            //}\n//        }\n\n//        public void CreateIndex()\n//        {\n//            store.ExecuteIndex(new TestDocCreation());\n//        }\n\n//        public void Dispose()\n//        {\n//            store.Dispose();\n//        }\n//        IEnumerable<TestDoc> MakeDoc()\n//        {\n//            foreach (var doc in Helper.GetDocs(_count))\n//            {\n//                var v = new TestDoc\n//                {\n//                    id = doc[\"_id\"].AsInt32,\n//                    name = doc[\"name\"].AsString,\n//                    lorem = doc[\"lorem\"].AsString\n//                };\n\n//                yield return v;\n//            }\n//        }\n\n\n//        public void Insert()\n//        {\n\n//            using (var bulk = store.BulkInsert())\n//            {\n//                foreach (var item in MakeDoc())\n//                {\n//                    bulk.Store(item);\n//                }\n//            }\n\n//        }\n\n//        public void Prepare()\n//        {\n//            if (isinmemory)\n//            {\n//                store = new EmbeddableDocumentStore { RunInMemory = true }.Initialize();\n//            }\n//            else\n//            {\n//                // store = new EmbeddableDocumentStore { RunInMemory = true }.Initialize();\n//            }\n//        }\n\n//        public void Query()\n//        {\n//            for (var i = 0; i < _count; i++)\n//            {\n//                using (var session = store.OpenSession())\n//                {\n//                    session.Load<TestDoc>(\"TestDoc/\" + i);\n//                }\n//            }\n//        }\n\n//        public void Update()\n//        {\n\n//        }\n//    }\n//}\n"
  },
  {
    "path": "sandbox/PerfTest2/Engines/SQLite_Test.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.SQLite;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace TestPerfLiteDB\n{\n    public class SQLite_Test : ITest\n    {\n        private string _filename;\n        private SQLiteConnection _db;\n        private int _count;\n\n        public int Count { get { return _count; } }\n        public int FileLength { get { return (int)new FileInfo(_filename).Length; } }\n\n        public SQLite_Test(int count, string password, bool journal, bool memory = false)\n        {\n            _count = count;\n            _filename = \"sqlite-\" + Guid.NewGuid().ToString(\"n\") + \".db\";\n            \n            if (memory)\n            {\n                var cs = \"Data Source=:memory:;New=True;\";\n                _db = new SQLiteConnection(cs);\n            }\n            else\n            {\n                var cs = \"Data Source=\" + _filename;\n                if (password != null) cs += \"; Password=\" + password;\n                if (journal == false) cs += \"; Journal Mode=Off\";\n                _db = new SQLiteConnection(cs);\n            }\n        }\n\n        public void Prepare()\n        {\n            _db.Open();\n\n            var table = new SQLiteCommand(\"CREATE TABLE col (id INTEGER NOT NULL PRIMARY KEY, name TEXT, lorem TEXT)\", _db);\n            table.ExecuteNonQuery();\n\n            var table2 = new SQLiteCommand(\"CREATE TABLE col_bulk (id INTEGER NOT NULL PRIMARY KEY, name TEXT, lorem TEXT)\", _db);\n            table2.ExecuteNonQuery();\n        }\n\n        public void Insert()\n        {\n            // standard insert is slow, mod same as Bulk\n            using (var trans = _db.BeginTransaction())\n            {\n                var cmd = new SQLiteCommand(\"INSERT INTO col (id, name, lorem) VALUES (@id, @name, @lorem)\", _db);\n\n                cmd.Parameters.Add(new SQLiteParameter(\"id\", DbType.Int32));\n                cmd.Parameters.Add(new SQLiteParameter(\"name\", DbType.String));\n                cmd.Parameters.Add(new SQLiteParameter(\"lorem\", DbType.String));\n\n                foreach (var doc in Helper.GetDocs(_count))\n                {\n                    cmd.Parameters[\"id\"].Value = doc[\"_id\"].AsInt32;\n                    cmd.Parameters[\"name\"].Value = doc[\"name\"].AsString;\n                    cmd.Parameters[\"lorem\"].Value = doc[\"lorem\"].AsString;\n\n                    cmd.ExecuteNonQuery();\n                }\n\n                trans.Commit();\n            }\n        }\n\n        public void Bulk()\n        {\n            using (var trans = _db.BeginTransaction())\n            {\n                var cmd = new SQLiteCommand(\"INSERT INTO col_bulk (id, name, lorem) VALUES (@id, @name, @lorem)\", _db);\n\n                cmd.Parameters.Add(new SQLiteParameter(\"id\", DbType.Int32));\n                cmd.Parameters.Add(new SQLiteParameter(\"name\", DbType.String));\n                cmd.Parameters.Add(new SQLiteParameter(\"lorem\", DbType.String));\n\n                foreach (var doc in Helper.GetDocs(_count))\n                {\n                    cmd.Parameters[\"id\"].Value = doc[\"_id\"].AsInt32;\n                    cmd.Parameters[\"name\"].Value = doc[\"name\"].AsString;\n                    cmd.Parameters[\"lorem\"].Value = doc[\"lorem\"].AsString;\n\n                    cmd.ExecuteNonQuery();\n                }\n\n                trans.Commit();\n            }\n        }\n\n        public void Update()\n        {\n            var cmd = new SQLiteCommand(\"UPDATE col SET name = @name, lorem = @lorem WHERE id = @id\", _db);\n\n            cmd.Parameters.Add(new SQLiteParameter(\"id\", DbType.Int32));\n            cmd.Parameters.Add(new SQLiteParameter(\"name\", DbType.String));\n            cmd.Parameters.Add(new SQLiteParameter(\"lorem\", DbType.String));\n\n            foreach (var doc in Helper.GetDocs(_count))\n            {\n                cmd.Parameters[\"id\"].Value = doc[\"_id\"].AsInt32;\n                cmd.Parameters[\"name\"].Value = doc[\"name\"].AsString;\n                cmd.Parameters[\"lorem\"].Value = doc[\"lorem\"].AsString;\n\n                cmd.ExecuteNonQuery();\n            }\n        }\n\n        public void CreateIndex()\n        {\n            var cmd = new SQLiteCommand(\"CREATE INDEX idx1 ON col (name)\", _db);\n\n            cmd.ExecuteNonQuery();\n        }\n\n        public void Query()\n        {\n            var cmd = new SQLiteCommand(\"SELECT * FROM col WHERE id = @id\", _db);\n\n            cmd.Parameters.Add(new SQLiteParameter(\"id\", DbType.Int32));\n\n            for (var i = 0; i < _count; i++)\n            {\n                cmd.Parameters[\"id\"].Value = i;\n\n                var r = cmd.ExecuteReader();\n\n                r.Read();\n\n                var name = r.GetString(1);\n                var lorem = r.GetString(2);\n\n                r.Close();\n            }\n        }\n\n        public void Delete()\n        {\n            var cmd = new SQLiteCommand(\"DELETE FROM col\", _db);\n\n            cmd.ExecuteNonQuery();\n        }\n\n        public void Drop()\n        {\n            var cmd = new SQLiteCommand(\"DROP TABLE col_bulk\", _db);\n\n            cmd.ExecuteNonQuery();\n        }\n\n        public void Dispose()\n        {\n            _db.Dispose();\n        }\n    }\n}\n"
  },
  {
    "path": "sandbox/PerfTest2/Generated/DatabaseBuilder.cs",
    "content": "// <auto-generated />\nusing LiteDB;\nusing MasterMemory;\nusing MessagePack;\nusing System.Collections.Generic;\nusing System.Data.SQLite;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System;\nusing TestPerfLiteDB;\nusing TestPerfLiteDB.Tables;\n\nnamespace TestPerfLiteDB\n{\n   public sealed class DatabaseBuilder : DatabaseBuilderBase\n   {\n        public DatabaseBuilder() : this(null) { }\n        public DatabaseBuilder(MessagePack.IFormatterResolver resolver) : base(resolver) { }\n\n        public DatabaseBuilder Append(System.Collections.Generic.IEnumerable<TestDoc> dataSource)\n        {\n            AppendCore(dataSource, x => x.id, System.Collections.Generic.Comparer<int>.Default);\n            return this;\n        }\n\n    }\n}"
  },
  {
    "path": "sandbox/PerfTest2/Generated/ImmutableBuilder.cs",
    "content": "// <auto-generated />\nusing LiteDB;\nusing MasterMemory;\nusing MessagePack;\nusing System.Collections.Generic;\nusing System.Data.SQLite;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System;\nusing TestPerfLiteDB;\nusing TestPerfLiteDB.Tables;\n\nnamespace TestPerfLiteDB\n{\n   public sealed class ImmutableBuilder : ImmutableBuilderBase\n   {\n        MemoryDatabase memory;\n\n        public ImmutableBuilder(MemoryDatabase memory)\n        {\n            this.memory = memory;\n        }\n\n        public MemoryDatabase Build()\n        {\n            return memory;\n        }\n\n        public void ReplaceAll(System.Collections.Generic.IList<TestDoc> data)\n        {\n            var newData = CloneAndSortBy(data, x => x.id, System.Collections.Generic.Comparer<int>.Default);\n            var table = new TestDocTable(newData);\n            memory = new MemoryDatabase(\n                table\n            \n            );\n        }\n\n        public void RemoveTestDoc(int[] keys)\n        {\n            var data = RemoveCore(memory.TestDocTable.GetRawDataUnsafe(), keys, x => x.id, System.Collections.Generic.Comparer<int>.Default);\n            var newData = CloneAndSortBy(data, x => x.id, System.Collections.Generic.Comparer<int>.Default);\n            var table = new TestDocTable(newData);\n            memory = new MemoryDatabase(\n                table\n            \n            );\n        }\n\n        public void Diff(TestDoc[] addOrReplaceData)\n        {\n            var data = DiffCore(memory.TestDocTable.GetRawDataUnsafe(), addOrReplaceData, x => x.id, System.Collections.Generic.Comparer<int>.Default);\n            var newData = CloneAndSortBy(data, x => x.id, System.Collections.Generic.Comparer<int>.Default);\n            var table = new TestDocTable(newData);\n            memory = new MemoryDatabase(\n                table\n            \n            );\n        }\n\n    }\n}"
  },
  {
    "path": "sandbox/PerfTest2/Generated/MasterMemoryResolver.cs",
    "content": "// <auto-generated />\nusing LiteDB;\nusing MasterMemory;\nusing MessagePack;\nusing System.Collections.Generic;\nusing System.Data.SQLite;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System;\nusing TestPerfLiteDB;\nusing TestPerfLiteDB.Tables;\n\nnamespace TestPerfLiteDB\n{\n    public class MasterMemoryResolver : global::MessagePack.IFormatterResolver\n    {\n        public static readonly global::MessagePack.IFormatterResolver Instance = new MasterMemoryResolver();\n\n        MasterMemoryResolver()\n        {\n\n        }\n\n        public global::MessagePack.Formatters.IMessagePackFormatter<T> GetFormatter<T>()\n        {\n            return FormatterCache<T>.formatter;\n        }\n\n        static class FormatterCache<T>\n        {\n            public static readonly global::MessagePack.Formatters.IMessagePackFormatter<T> formatter;\n\n            static FormatterCache()\n            {\n                var f = MasterMemoryResolverGetFormatterHelper.GetFormatter(typeof(T));\n                if (f != null)\n                {\n                    formatter = (global::MessagePack.Formatters.IMessagePackFormatter<T>)f;\n                }\n            }\n        }\n    }\n\n    internal static class MasterMemoryResolverGetFormatterHelper\n    {\n        static readonly global::System.Collections.Generic.Dictionary<Type, int> lookup;\n\n        static MasterMemoryResolverGetFormatterHelper()\n        {\n            lookup = new global::System.Collections.Generic.Dictionary<Type, int>(1)\n            {\n                {typeof(TestDoc[]), 0 },\n            };\n        }\n\n        internal static object GetFormatter(Type t)\n        {\n            int key;\n            if (!lookup.TryGetValue(t, out key)) return null;\n\n            switch (key)\n            {\n                case 0: return new MessagePack.Formatters.ArrayFormatter<TestDoc>();\n                default: return null;\n            }\n        }\n    }\n}"
  },
  {
    "path": "sandbox/PerfTest2/Generated/MemoryDatabase.cs",
    "content": "// <auto-generated />\nusing LiteDB;\nusing MasterMemory;\nusing MessagePack;\nusing System.Collections.Generic;\nusing System.Data.SQLite;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System;\nusing TestPerfLiteDB;\nusing TestPerfLiteDB.Tables;\n\nnamespace TestPerfLiteDB\n{\n   public sealed class MemoryDatabase : MemoryDatabaseBase\n   {\n        public TestDocTable TestDocTable { get; private set; }\n\n        public MemoryDatabase(\n            TestDocTable TestDocTable\n        )\n        {\n            this.TestDocTable = TestDocTable;\n        }\n\n        public MemoryDatabase(byte[] databaseBinary, bool internString = true, MessagePack.IFormatterResolver formatterResolver = null, int maxDegreeOfParallelism = 1)\n            : base(databaseBinary, internString, formatterResolver, maxDegreeOfParallelism)\n        {\n        }\n\n        protected override void Init(Dictionary<string, (int offset, int count)> header, System.ReadOnlyMemory<byte> databaseBinary, MessagePack.MessagePackSerializerOptions options, int maxDegreeOfParallelism)\n        {\n            this.TestDocTable = ExtractTableData<TestDoc, TestDocTable>(header, databaseBinary, options, xs => new TestDocTable(xs));\n        }\n\n        public ImmutableBuilder ToImmutableBuilder()\n        {\n            return new ImmutableBuilder(this);\n        }\n\n        public DatabaseBuilder ToDatabaseBuilder()\n        {\n            var builder = new DatabaseBuilder();\n            builder.Append(this.TestDocTable.GetRawDataUnsafe());\n            return builder;\n        }\n    }\n}"
  },
  {
    "path": "sandbox/PerfTest2/Generated/Tables/TestDocTable.cs",
    "content": "// <auto-generated />\nusing LiteDB;\nusing MasterMemory;\nusing MessagePack;\nusing System.Collections.Generic;\nusing System.Data.SQLite;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System;\nusing TestPerfLiteDB;\n\nnamespace TestPerfLiteDB.Tables\n{\n   public sealed partial class TestDocTable : TableBase<TestDoc>\n   {\n        readonly Func<TestDoc, int> primaryIndexSelector;\n\n\n        public TestDocTable(TestDoc[] sortedData)\n            : base(sortedData)\n        {\n            this.primaryIndexSelector = x => x.id;\n        }\n\n\n        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]\n        public TestDoc FindByid(int key)\n        {\n            var lo = 0;\n            var hi = data.Length - 1;\n            while (lo <= hi)\n            {\n                var mid = (int)(((uint)hi + (uint)lo) >> 1);\n                var selected = data[mid].id;\n                var found = (selected < key) ? -1 : (selected > key) ? 1 : 0;\n                if (found == 0) { return data[mid]; }\n                if (found < 0) { lo = mid + 1; }\n                else { hi = mid - 1; }\n            }\n            return default;\n        }\n\n        public TestDoc FindClosestByid(int key, bool selectLower = true)\n        {\n            return FindUniqueClosestCore(data, primaryIndexSelector, System.Collections.Generic.Comparer<int>.Default, key, selectLower);\n        }\n\n        public RangeView<TestDoc> FindRangeByid(int min, int max, bool ascendant = true)\n        {\n            return FindUniqueRangeCore(data, primaryIndexSelector, System.Collections.Generic.Comparer<int>.Default, min, max, ascendant);\n        }\n\n    }\n}"
  },
  {
    "path": "sandbox/PerfTest2/PerfTest2.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<OutputType>Exe</OutputType>\n\t\t<TargetFramework>net9.0</TargetFramework>\n\t\t<NoWarn>1701;1702;NU1904</NoWarn>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<PackageReference Include=\"LiteDB\" Version=\"4.1.4\" />\n\t\t<PackageReference Include=\"System.Data.SQLite\" Version=\"1.0.111\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<ProjectReference Include=\"..\\..\\src\\MasterMemory\\MasterMemory.csproj\" />\n\t\t<ProjectReference Include=\"..\\..\\src\\MasterMemory.SourceGenerator\\MasterMemory.SourceGenerator.csproj\">\n\t\t\t<OutputItemType>Analyzer</OutputItemType>\n\t\t\t<ReferenceOutputAssembly>false</ReferenceOutputAssembly>\n\t\t</ProjectReference>\n\t</ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "sandbox/PerfTest2/Program.cs",
    "content": "﻿#pragma warning disable\nusing System;\nusing System.Collections.Generic;\nusing System.Data.SQLite;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing LiteDB;\nusing System.IO;\n\n// based test code -> https://github.com/mbdavid/LiteDB-Perf\n\nnamespace TestPerfLiteDB\n{\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            RunTest(\"LiteDB: default\", new LiteDB_Test(5000, null, new LiteDB.FileOptions { Journal = true, FileMode = LiteDB.FileMode.Shared }));\n            RunTest(\"LiteDB: encrypted\", new LiteDB_Test(5000, \"mypass\", new LiteDB.FileOptions { Journal = true, FileMode = LiteDB.FileMode.Shared }));\n            RunTest(\"LiteDB: exclusive no journal\", new LiteDB_Test(5000, null, new LiteDB.FileOptions { Journal = false, FileMode = LiteDB.FileMode.Exclusive }));\n            RunTest(\"LiteDB: in-memory\", new LiteDB_Test(5000));\n\n            RunTest(\"SQLite: default\", new SQLite_Test(5000, null, true));\n            //RunTest(\"SQLite: encrypted\", new SQLite_Test(5000, \"mypass\", true));\n            //RunTest(\"SQLite: no journal\", new SQLite_Test(5000, null, false));\n            RunTest(\"SQLite: in-memory\", new SQLite_Test(5000, null, false, true));\n\n\n\n            RunTest(\"Dictionary\", new Dictionary_Test(5000));\n            RunTest(\"ConcurrentDictionary\", new ConcurrentDictionary_Test(5000));\n            RunTest(\"ImmutableDictionary\", new ImmutableDictionary_Test(5000));\n\n            RunTest(\"MasterMemory\", new MasterMemory_Test(5000));\n\n            Console.ReadKey();\n\n            // RunTest(\"RavenDB: in-memory\", new RavenDB_Test(5000, true));\n        }\n\n        static void RunTest(string name, ITest test)\n        {\n            var title = name + \" - \" + test.Count + \" records\";\n            Console.WriteLine(title);\n            Console.WriteLine(\"=\".PadLeft(title.Length, '='));\n\n            test.Prepare();\n\n            test.Run(\"Insert\", test.Insert, true);\n            test.Run(\"Bulk\", test.Bulk, true);\n            test.Run(\"CreateIndex\", test.CreateIndex, true);\n            test.Run(\"Query\", test.Query, false);\n            test.Run(\"Query\", test.Query, false);\n            test.Run(\"Query\", test.Query, false);\n            test.Run(\"Query\", test.Query, false);\n\n            try\n            {\n                Console.WriteLine(\"FileLength     : \" + Math.Round((double)test.FileLength / (double)1024, 2).ToString().PadLeft(5, ' ') + \" kb\");\n            }\n            catch (System.IO.FileNotFoundException)\n            {\n            }\n\n            test.Dispose();\n\n            Console.WriteLine();\n\n        }\n    }\n}\n"
  },
  {
    "path": "sandbox/PerfTest2/Utils/Helper.cs",
    "content": "﻿#pragma warning disable\nusing System;\nusing System.Collections.Generic;\nusing System.Data.SQLite;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing LiteDB;\nusing MasterMemory;\nusing MessagePack;\n\nnamespace TestPerfLiteDB\n{\n    [MemoryTable(\"TestDoc\"), MessagePackObject(true)]\n    public class TestDoc\n    {\n        [PrimaryKey]\n        public int id { get; set; }\n        public string name { get; set; }\n        public string lorem { get; set; }\n    }\n\n    static class Helper\n    {\n        public static void Run(this ITest test, string name, Action action, bool dryrun)\n        {\n            var sw = new Stopwatch();\n\n            System.GC.Collect(2, GCCollectionMode.Forced, blocking: true);\n            sw.Start();\n            action();\n            sw.Stop();\n\n            var time = sw.ElapsedMilliseconds.ToString().PadLeft(5, ' ');\n            var seg = Math.Round(test.Count / sw.Elapsed.TotalSeconds).ToString().PadLeft(8, ' ');\n\n            if (!dryrun)\n            {\n                Console.WriteLine(name.PadRight(15, ' ') + \": \" +\n                    time + \" ms - \" +\n                    seg + \" records/second\");\n            }\n        }\n\n        public static IEnumerable<BsonDocument> GetDocs(int count)\n        {\n            for (var i = 0; i < count; i++)\n            {\n                yield return new BsonDocument\n                {\n                    { \"_id\", i },\n                    { \"name\", Guid.NewGuid().ToString() },\n                    { \"lorem\", LoremIpsum(3, 5, 2, 3, 3) }\n                };\n            }\n        }\n\n        public static string LoremIpsum(int minWords, int maxWords,\n            int minSentences, int maxSentences,\n            int numParagraphs)\n        {\n            var words = new[] { \"lorem\", \"ipsum\", \"dolor\", \"sit\", \"amet\", \"consectetuer\",\n                \"adipiscing\", \"elit\", \"sed\", \"diam\", \"nonummy\", \"nibh\", \"euismod\",\n                \"tincidunt\", \"ut\", \"laoreet\", \"dolore\", \"magna\", \"aliquam\", \"erat\" };\n\n            var rand = new Random(DateTime.Now.Millisecond);\n            var numSentences = rand.Next(maxSentences - minSentences) + minSentences + 1;\n            var numWords = rand.Next(maxWords - minWords) + minWords + 1;\n\n            var result = new StringBuilder();\n\n            for (int p = 0; p < numParagraphs; p++)\n            {\n                for (int s = 0; s < numSentences; s++)\n                {\n                    for (int w = 0; w < numWords; w++)\n                    {\n                        if (w > 0) { result.Append(\" \"); }\n                        result.Append(words[rand.Next(words.Length)]);\n                    }\n                    result.Append(\". \");\n                }\n                result.AppendLine();\n            }\n\n            return result.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "src/MasterMemory/DatabaseBuilderBase.cs",
    "content": "﻿using MasterMemory.Internal;\nusing MessagePack;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Reflection;\nusing System.Runtime.InteropServices;\n\nnamespace MasterMemory\n{\n    public abstract class DatabaseBuilderBase\n    {\n        readonly ByteBufferWriter bufferWriter = new ByteBufferWriter();\n\n        // TableName, (Offset, Count)\n        readonly Dictionary<string, (int offset, int count)> header = new Dictionary<string, (int offset, int count)>();\n        readonly MessagePackSerializerOptions? options;\n\n        public DatabaseBuilderBase(MessagePackSerializerOptions? options)\n        {\n            // options keep null to lazily use default options\n            if (options != null)\n            {\n                options = options.WithCompression(MessagePackCompression.Lz4Block);\n            }\n        }\n\n        public DatabaseBuilderBase(IFormatterResolver? resolver)\n        {\n            if (resolver != null)\n            {\n                this.options = MessagePackSerializer.DefaultOptions\n                    .WithCompression(MessagePackCompression.Lz4Block)\n                    .WithResolver(resolver);\n            }\n\n        }\n\n        protected void AppendCore<T, TKey>(IEnumerable<T> datasource, Func<T, TKey> indexSelector, IComparer<TKey> comparer)\n        {\n            var tableName = typeof(T).GetCustomAttribute<MemoryTableAttribute>();\n            if (tableName == null) throw new InvalidOperationException(\"Type is not annotated MemoryTableAttribute. Type:\" + typeof(T).FullName);\n\n            if (header.ContainsKey(tableName.TableName))\n            {\n                throw new InvalidOperationException(\"TableName is already appended in builder. TableName: \" + tableName.TableName + \" Type:\" + typeof(T).FullName);\n            }\n\n            if (datasource == null) return;\n\n            // sort(as indexed data-table)\n            var source = FastSort(datasource, indexSelector, comparer);\n\n            // write data and store header-data.\n            var useOption = options ?? MessagePackSerializer.DefaultOptions.WithCompression(MessagePackCompression.Lz4Block);\n\n            var offset = bufferWriter.CurrentOffset;\n            MessagePackSerializer.Serialize(bufferWriter, source, useOption);\n\n            header.Add(tableName.TableName, (offset, bufferWriter.CurrentOffset - offset));\n        }\n\n        static TElement[] FastSort<TElement, TKey>(IEnumerable<TElement> datasource, Func<TElement, TKey> indexSelector, IComparer<TKey> comparer)\n        {\n            var collection = datasource as ICollection<TElement>;\n            if (collection != null)\n            {\n                var array = new TElement[collection.Count];\n                var sortSource = new TKey[collection.Count];\n                var i = 0;\n                foreach (var item in collection)\n                {\n                    array[i] = item;\n                    sortSource[i] = indexSelector(item);\n                    i++;\n                }\n                Array.Sort(sortSource, array, 0, collection.Count, comparer);\n                return array;\n            }\n            else\n            {\n                var array = new ExpandableArray<TElement>(null!);\n                var sortSource = new ExpandableArray<TKey>(null!);\n                foreach (var item in datasource)\n                {\n                    array.Add(item);\n                    sortSource.Add(indexSelector(item));\n                }\n\n                Array.Sort(sortSource.items, array.items, 0, array.count, comparer);\n\n                Array.Resize(ref array.items, array.count);\n                return array.items;\n            }\n        }\n\n        public byte[] Build()\n        {\n            using (var ms = new MemoryStream())\n            {\n                WriteToStream(ms);\n                return ms.ToArray();\n            }\n        }\n\n        public void WriteToStream(Stream stream)\n        {\n            MessagePackSerializer.Serialize(stream, header, HeaderFormatterResolver.StandardOptions);\n            MemoryMarshal.TryGetArray(bufferWriter.WrittenMemory, out var segment);\n            stream.Write(segment.Array, segment.Offset, segment.Count);\n        }\n    }\n}"
  },
  {
    "path": "src/MasterMemory/DatabaseBuilderBaseExtensions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace MasterMemory\n{\n    public static class DatabaseBuilderExtensions\n    {\n        public static void AppendDynamic(this DatabaseBuilderBase builder, Type dataType, IList<object> tableData)\n        {\n            var appendMethod = builder.GetType().GetMethods()\n                .Where(x => x.Name == \"Append\")\n                .Where(x => x.GetParameters()[0].ParameterType.GetGenericArguments()[0] == dataType)\n                .FirstOrDefault();\n\n            if (appendMethod == null)\n            {\n                throw new InvalidOperationException(\"Append(IEnumerable<DataType>) can not found. DataType:\" + dataType);\n            }\n\n            var dynamicArray = Array.CreateInstance(dataType, tableData.Count);\n            for (int i = 0; i < tableData.Count; i++)\n            {\n                dynamicArray.SetValue(Convert.ChangeType(tableData[i], dataType), i);\n            }\n\n            appendMethod.Invoke(builder, new object[] { dynamicArray });\n        }\n    }\n}"
  },
  {
    "path": "src/MasterMemory/IValidatable.cs",
    "content": "﻿using MasterMemory.Validation;\nusing System;\nusing System.Linq.Expressions;\n\nnamespace MasterMemory\n{\n    public interface IValidatable<TSelf>\n    {\n        void Validate(IValidator<TSelf> validator);\n    }\n\n    public interface IValidator<T>\n    {\n        ValidatableSet<T> GetTableSet();\n        ReferenceSet<T, TRef> GetReferenceSet<TRef>();\n        void Validate(Expression<Func<T, bool>> predicate);\n        void Validate(Func<T, bool> predicate, string message);\n        void ValidateAction(Expression<Func<bool>> predicate);\n        void ValidateAction(Func<bool> predicate, string message);\n        void Fail(string message);\n        bool CallOnce();\n    }\n}"
  },
  {
    "path": "src/MasterMemory/ImmutableBuilderBase.cs",
    "content": "﻿using MasterMemory.Internal;\nusing System;\nusing System.Collections.Generic;\n\nnamespace MasterMemory\n{\n    public abstract class ImmutableBuilderBase\n    {\n        static protected TElement[] CloneAndSortBy<TElement, TKey>(IList<TElement> elementData, Func<TElement, TKey> indexSelector, IComparer<TKey> comparer)\n        {\n            var array = new TElement[elementData.Count];\n            var sortSource = new TKey[elementData.Count];\n            for (int i = 0; i < elementData.Count; i++)\n            {\n                array[i] = elementData[i];\n                sortSource[i] = indexSelector(elementData[i]);\n            }\n\n            Array.Sort(sortSource, array, 0, array.Length, comparer);\n            return array;\n        }\n\n        static protected List<TElement> RemoveCore<TElement, TKey>(TElement[] array, TKey[] keys, Func<TElement, TKey> keySelector, IComparer<TKey> comparer)\n        {\n            var removeIndexes = new HashSet<int>();\n            foreach (var key in keys)\n            {\n                var index = BinarySearch.FindFirst(array, key, keySelector, comparer);\n                if (index != -1)\n                {\n                    removeIndexes.Add(index);\n                }\n            }\n\n            var newList = new List<TElement>(array.Length - removeIndexes.Count);\n            for (int i = 0; i < array.Length; i++)\n            {\n                if (!removeIndexes.Contains(i))\n                {\n                    newList.Add(array[i]);\n                }\n            }\n\n            return newList;\n        }\n\n        static protected List<TElement> DiffCore<TElement, TKey>(TElement[] array, TElement[] addOrReplaceData, Func<TElement, TKey> keySelector, IComparer<TKey> comparer)\n        {\n            var newList = new List<TElement>(array.Length);\n            var replaceIndexes = new Dictionary<int, TElement>();\n            foreach (var data in addOrReplaceData)\n            {\n                var index = BinarySearch.FindFirst(array, keySelector(data), keySelector, comparer);\n                if (index != -1)\n                {\n                    replaceIndexes.Add(index, data);\n                }\n                else\n                {\n                    newList.Add(data);\n                }\n            }\n\n            for (int i = 0; i < array.Length; i++)\n            {\n                if (replaceIndexes.TryGetValue(i, out var data))\n                {\n                    newList.Add(data);\n                }\n                else\n                {\n                    newList.Add(array[i]);\n                }\n            }\n\n            return newList;\n        }\n    }\n}"
  },
  {
    "path": "src/MasterMemory/Internal/BinarySearch.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nnamespace MasterMemory.Internal\n{\n    internal static class BinarySearch\n    {\n        public static int FindFirst<T, TKey>(T[] array, TKey key, Func<T, TKey> selector, IComparer<TKey> comparer)\n        {\n            var lo = 0;\n            var hi = array.Length - 1;\n\n            while (lo <= hi)\n            {\n                var mid = (int)(((uint)hi + (uint)lo) >> 1);\n                var found = comparer.Compare(selector(array[mid]), key);\n\n                if (found == 0) return mid;\n                if (found < 0)\n                {\n                    lo = mid + 1;\n                }\n                else\n                {\n                    hi = mid - 1;\n                }\n            }\n\n            return -1;\n        }\n\n        public static int FindFirstIntKey<T>(T[] array, int key, Func<T, int> selector)\n        {\n            var lo = 0;\n            var hi = array.Length - 1;\n\n            while (lo <= hi)\n            {\n                var mid = (int)(((uint)hi + (uint)lo) >> 1);\n                // compare inlining\n                var selectedValue = selector(array[mid]);\n                var found = (selectedValue < key) ? -1 : (selectedValue > key) ? 1 : 0;\n\n                if (found == 0) return mid;\n                if (found < 0)\n                {\n                    lo = mid + 1;\n                }\n                else\n                {\n                    hi = mid - 1;\n                }\n            }\n\n            return -1;\n        }\n\n        // lo = 0, hi = Count.\n        public static int FindClosest<T, TKey>(T[] array, int lo, int hi, TKey key, Func<T, TKey> selector, IComparer<TKey> comparer, bool selectLower)\n        {\n            if (array.Length == 0) return -1;\n\n            lo = lo - 1;\n\n            while (hi - lo > 1)\n            {\n                var mid = lo + ((hi - lo) >> 1);\n                var found = comparer.Compare(selector(array[mid]), key);\n\n                if (found == 0)\n                {\n                    lo = hi = mid;\n                    break;\n                }\n                if (found >= 1)\n                {\n                    hi = mid;\n                }\n                else\n                {\n                    lo = mid;\n                }\n            }\n\n            return selectLower ? lo : hi;\n        }\n\n        // default lo = 0, hi = array.Count\n        public static int LowerBound<T, TKey>(T[] array, int lo, int hi, TKey key, Func<T, TKey> selector, IComparer<TKey> comparer)\n        {\n            while (lo < hi)\n            {\n                var mid = lo + ((hi - lo) >> 1);\n                var found = comparer.Compare(key, selector(array[mid]));\n\n                if (found <= 0)\n                {\n                    hi = mid;\n                }\n                else\n                {\n                    lo = mid + 1;\n                }\n            }\n\n            var index = lo;\n            if (index == -1 || array.Length <= index)\n            {\n                return -1;\n            }\n\n            // check final\n            return (comparer.Compare(key, selector(array[index])) == 0)\n                ? index\n                : -1;\n        }\n\n        public static int UpperBound<T, TKey>(T[] array, int lo, int hi, TKey key, Func<T, TKey> selector, IComparer<TKey> comparer)\n        {\n            while (lo < hi)\n            {\n                var mid = lo + ((hi - lo) >> 1);\n                var found = comparer.Compare(key, selector(array[mid]));\n\n                if (found >= 0)\n                {\n                    lo = mid + 1;\n                }\n                else\n                {\n                    hi = mid;\n                }\n            }\n\n            var index = (lo == 0) ? 0 : lo - 1;\n            if (index == -1 || array.Length <= index)\n            {\n                return -1;\n            }\n\n            // check final\n            return (comparer.Compare(key, selector(array[index])) == 0)\n                ? index\n                : -1;\n        }\n\n\n        //... want the lowest index of  Key <= Value\n        //... returns 0 if key is <= all values in array\n        //... returns array.Length if key is > all values in array\n\n        public static int LowerBoundClosest<T, TKey>(T[] array, int lo, int hi, TKey key, Func<T, TKey> selector, IComparer<TKey> comparer)\n        {\n            while (lo < hi)\n            {\n                var mid = lo + ((hi - lo) >> 1);\n                var found = comparer.Compare(key, selector(array[mid]));\n\n                if (found <= 0)     //... Key is <= value at mid\n                {\n                    hi = mid;\n                }\n                else\n                {\n                    lo = mid + 1;   //... Notice that lo starts at zero and can only increase\n                }\n            }\n\n            var index = lo;         //... index will always be zero or greater\n\n            if ( array.Length <= index)\n            {\n               return array.Length;\n            }\n\n            // check final\n            return (comparer.Compare(key, selector(array[index])) <= 0)\n                ? index\n                : -1;\n        }\n\n \n        //... want the highest index of  Key >= Value\n        //... returns -1 if key is < than all values in array\n        //... returns array.Length - 1 if key is >= than all values in array\n\n        public static int UpperBoundClosest<T, TKey>(T[] array, int lo, int hi, TKey key, Func<T, TKey> selector, IComparer<TKey> comparer)\n        {\n            while (lo < hi)\n            {\n                var mid = lo + ((hi - lo) >> 1);\n                var found = comparer.Compare(key, selector(array[mid]));\n\n                if (found >= 0)     //... Key >= value at mid\n                {\n                    lo = mid + 1;   //... Note lo starts at zero and can only increase\n                }\n                else\n                {\n                    hi = mid;\n                }\n            }\n\n            var index = (lo == 0) ? 0 : lo - 1;   //... index will always be zero or greater\n\n            if ( index >= array.Length )\n            {\n               return array.Length;\n            }\n\n            // check final\n            return (comparer.Compare(key, selector(array[index])) >= 0)\n                ? index\n                : -1;\n        }\n\n\n\n    }\n}"
  },
  {
    "path": "src/MasterMemory/Internal/ByteBufferWriter.cs",
    "content": "﻿using System;\nusing System.Buffers;\n\nnamespace MasterMemory\n{\n    internal class ByteBufferWriter : IBufferWriter<byte>\n    {\n        byte[] buffer;\n        int index;\n\n        public int CurrentOffset => index;\n        public ReadOnlySpan<byte> WrittenSpan => buffer.AsSpan(0, index);\n        public ReadOnlyMemory<byte> WrittenMemory => new ReadOnlyMemory<byte>(buffer, 0, index);\n\n        public ByteBufferWriter()\n        {\n            buffer = new byte[1024];\n            index = 0;\n        }\n\n        public void Advance(int count)\n        {\n            index += count;\n        }\n\n        public Memory<byte> GetMemory(int sizeHint = 0)\n        {\n            AGAIN:\n            var nextSize = index + sizeHint;\n            if (buffer.Length < nextSize)\n            {\n                Array.Resize(ref buffer, Math.Max(buffer.Length * 2, nextSize));\n            }\n\n            if (sizeHint == 0)\n            {\n                var result = new Memory<byte>(buffer, index, buffer.Length - index);\n                if (result.Length == 0)\n                {\n                    sizeHint = 1024;\n                    goto AGAIN;\n                }\n                return result;\n            }\n            else\n            {\n                return new Memory<byte>(buffer, index, sizeHint);\n            }\n        }\n\n        public Span<byte> GetSpan(int sizeHint = 0)\n        {\n            return GetMemory(sizeHint).Span;\n        }\n    }\n}"
  },
  {
    "path": "src/MasterMemory/Internal/ExpandableArray.cs",
    "content": "﻿using System;\n\nnamespace MasterMemory.Internal\n{\n    internal struct ExpandableArray<TElement>\n    {\n        internal TElement[] items;\n        internal int count;\n\n        public ExpandableArray(object dummy)\n        {\n            items = Array.Empty<TElement>();\n            count = 0;\n        }\n\n        internal void Add(TElement item)\n        {\n            if (items == null || items.Length == 0)\n            {\n                items = new TElement[4];\n            }\n            else if (items.Length == (count + 1))\n            {\n                Array.Resize(ref items, checked(count * 2));\n            }\n            items[count++] = item;\n        }\n    }\n}"
  },
  {
    "path": "src/MasterMemory/Internal/HeaderFormatterResolver.cs",
    "content": "﻿using MessagePack;\nusing MessagePack.Formatters;\nusing System;\nusing System.Collections.Generic;\n\nnamespace MasterMemory.Internal\n{\n    // for AOT(IL2CPP) concrete generic formatter.\n    internal class HeaderFormatterResolver : IFormatterResolver\n    {\n        public static readonly IFormatterResolver Instance = new HeaderFormatterResolver();\n        public static readonly MessagePackSerializerOptions StandardOptions = MessagePackSerializerOptions.Standard.WithResolver(Instance);\n\n        public IMessagePackFormatter<T>? GetFormatter<T>()\n        {\n            if (typeof(T) == typeof(Dictionary<string, (int, int)>))\n            {\n                return (IMessagePackFormatter<T>)(object)new DictionaryFormatter<string, (int, int)>();\n            }\n            else if (typeof(T) == typeof(string))\n            {\n                return (IMessagePackFormatter<T>)(object)NullableStringFormatter.Instance;\n            }\n            else if (typeof(T) == typeof((int, int)))\n            {\n                return (IMessagePackFormatter<T>)(object)new IntIntValueTupleFormatter();\n            }\n            else if (typeof(T) == typeof(int))\n            {\n                return (IMessagePackFormatter<T>)(object)Int32Formatter.Instance;\n            }\n\n            return null;\n        }\n    }\n\n    internal sealed class IntIntValueTupleFormatter : IMessagePackFormatter<ValueTuple<int, int>>\n    {\n        public void Serialize(ref MessagePackWriter writer, (int, int) value, MessagePackSerializerOptions options)\n        {\n            writer.WriteArrayHeader(2);\n            writer.WriteInt32(value.Item1);\n            writer.WriteInt32(value.Item2);\n        }\n\n        public (int, int) Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)\n        {\n            if (reader.IsNil)\n            {\n                throw new InvalidOperationException(\"Data is Nil, ValueTuple can not be null.\");\n            }\n\n            var count = reader.ReadArrayHeader();\n            if (count != 2) throw new InvalidOperationException(\"Invalid ValueTuple count\");\n\n            var item1 = reader.ReadInt32();\n            var item2 = reader.ReadInt32();\n\n            return new ValueTuple<int, int>(item1, item2);\n        }\n    }\n}"
  },
  {
    "path": "src/MasterMemory/Internal/InternStringResolver.cs",
    "content": "﻿using MessagePack;\nusing MessagePack.Formatters;\nusing System;\n\nnamespace MasterMemory.Internal\n{\n#pragma warning disable MsgPack013 // Inaccessible formatter\n    internal class InternStringResolver : IFormatterResolver, IMessagePackFormatter<string?>\n    {\n        readonly IFormatterResolver innerResolver;\n\n        public InternStringResolver(IFormatterResolver innerResolver)\n        {\n            this.innerResolver = innerResolver;\n        }\n\n        public IMessagePackFormatter<T>? GetFormatter<T>()\n        {\n            if (typeof(T) == typeof(string))\n            {\n                return (IMessagePackFormatter<T>)this;\n            }\n\n            return innerResolver.GetFormatter<T>();\n        }\n\n        string? IMessagePackFormatter<string?>.Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)\n        {\n            var str = reader.ReadString();\n            if (str == null)\n            {\n                return null;\n            }\n\n            return string.Intern(str);\n        }\n\n        void IMessagePackFormatter<string?>.Serialize(ref MessagePackWriter writer, string? value, MessagePackSerializerOptions options)\n        {\n            throw new NotImplementedException();\n        }\n    }\n}"
  },
  {
    "path": "src/MasterMemory/MasterMemory.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFramework>netstandard2.0</TargetFramework>\n\t\t<RootNamespace>MasterMemory</RootNamespace>\n\t\t<AssemblyName>MasterMemory</AssemblyName>\n\t\t<LangVersion>13</LangVersion>\n\t\t<Nullable>enable</Nullable>\n\t\t<OutputType>Library</OutputType>\n\t\t<GeneratePackageOnBuild>False</GeneratePackageOnBuild>\n\t\t<Company>Cysharp</Company>\n\t\t<GenerateDocumentationFile>true</GenerateDocumentationFile>\n\t\t<NoWarn>1701;1702;1705;1591</NoWarn>\n\n\t\t<!-- NuGet -->\n\t\t<PackageId>MasterMemory</PackageId>\n\t\t<Description>Embedded Typed Readonly In-Memory Document Database for .NET Core and Unity.</Description>\n\t\t<IsPackable>true</IsPackable>\n\n\t\t<!-- SourceGenerator Packaging configs... -->\n\t\t<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);PackBuildOutputs</TargetsForTfmSpecificContentInPackage>\n\t</PropertyGroup>\n\n\t<!-- for nuget publish -->\n\t<Target Name=\"PackBuildOutputs\" DependsOnTargets=\"SatelliteDllsProjectOutputGroup;DebugSymbolsProjectOutputGroup\">\n\t\t<ItemGroup>\n\t\t\t<TfmSpecificPackageFile Include=\"$(TargetDir)\\MasterMemory.SourceGenerator.dll\" PackagePath=\"analyzers\\dotnet\\cs\" />\n\t\t\t<TfmSpecificPackageFile Include=\"@(SatelliteDllsProjectOutputGroupOutput->'%(FinalOutputPath)')\" PackagePath=\"analyzers\\dotnet\\cs\\%(SatelliteDllsProjectOutputGroupOutput.Culture)\\\" />\n\t\t</ItemGroup>\n\t</Target>\n\n\t<ItemGroup>\n\t\t<PackageReference Include=\"MessagePack\" Version=\"3.1.3\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<ProjectReference Include=\"..\\MasterMemory.Annotations\\MasterMemory.Annotations.csproj\" />\n\n\t\t<!-- We don't consume the analyzers in this library itself,\n         but we reference the project to add a package dependency so users of this library will automatically get the analyzers. -->\n\t\t<ProjectReference Include=\"..\\MasterMemory.SourceGenerator\\MasterMemory.SourceGenerator.csproj\" PrivateAssets=\"all\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<None Update=\"Validation\\ValidatableSet.Sequential.tt\">\n\t\t\t<Generator>TextTemplatingFileGenerator</Generator>\n\t\t\t<LastGenOutput>ValidatableSet.Sequential.cs</LastGenOutput>\n\t\t</None>\n\t</ItemGroup>\n\t<ItemGroup>\n\t\t<Service Include=\"{508349b6-6b84-4df5-91f0-309beebad82d}\" />\n\t</ItemGroup>\n\t<ItemGroup>\n\t\t<Compile Update=\"Validation\\ValidatableSet.Sequential.cs\">\n\t\t\t<DesignTime>True</DesignTime>\n\t\t\t<AutoGen>True</AutoGen>\n\t\t\t<DependentUpon>ValidatableSet.Sequential.tt</DependentUpon>\n\t\t</Compile>\n\t</ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "src/MasterMemory/MemoryDatabaseBase.cs",
    "content": "﻿using MasterMemory.Internal;\nusing MessagePack;\nusing MessagePack.Formatters;\nusing System;\nusing System.Collections.Generic;\nusing System.Reflection;\nusing System.Linq;\nusing System.Buffers;\nusing MasterMemory.Validation;\n\nnamespace MasterMemory\n{\n    public abstract class MemoryDatabaseBase\n    {\n        protected MemoryDatabaseBase()\n        {\n\n        }\n\n        public MemoryDatabaseBase(byte[] databaseBinary, bool internString = true, IFormatterResolver? formatterResolver = null, int maxDegreeOfParallelism = 1)\n        {\n            var reader = new MessagePackReader(databaseBinary);\n            var formatter = new DictionaryFormatter<string, (int, int)>();\n\n            var header = formatter.Deserialize(ref reader, HeaderFormatterResolver.StandardOptions);\n            var resolver = formatterResolver ?? MessagePackSerializer.DefaultOptions.Resolver;\n            if (internString)\n            {\n                resolver = new InternStringResolver(resolver);\n            }\n            if (maxDegreeOfParallelism < 1)\n            {\n                maxDegreeOfParallelism = 1;\n            }\n\n            Init(header!, databaseBinary.AsMemory((int)reader.Consumed), MessagePackSerializer.DefaultOptions.WithResolver(resolver).WithCompression(MessagePackCompression.Lz4Block), maxDegreeOfParallelism);\n        }\n\n        protected static TView ExtractTableData<T, TView>(Dictionary<string, (int offset, int count)> header, ReadOnlyMemory<byte> databaseBinary, MessagePackSerializerOptions options, Func<T[], TView> createView)\n        {\n            var tableName = typeof(T).GetCustomAttribute<MemoryTableAttribute>();\n            if (tableName == null) throw new InvalidOperationException(\"Type is not annotated MemoryTableAttribute. Type:\" + typeof(T).FullName);\n\n            if (header.TryGetValue(tableName.TableName, out var segment))\n            {\n                var data = MessagePackSerializer.Deserialize<T[]>(databaseBinary.Slice(segment.offset, segment.count), options);\n                return createView(data);\n            }\n            else\n            {\n                // return empty\n                var data = Array.Empty<T>();\n                return createView(data);\n            }\n        }\n\n        protected abstract void Init(Dictionary<string, (int offset, int count)> header, ReadOnlyMemory<byte> databaseBinary, MessagePackSerializerOptions options, int maxDegreeOfParallelism);\n\n        public static TableInfo[] GetTableInfo(byte[] databaseBinary, bool storeTableData = true)\n        {\n            var formatter = new DictionaryFormatter<string, (int, int)>();\n            var reader = new MessagePackReader(databaseBinary);\n            var header = formatter.Deserialize(ref reader, HeaderFormatterResolver.StandardOptions);\n\n            return header.Select(x => new TableInfo(x.Key, x.Value.Item2, storeTableData ? databaseBinary : null, x.Value.Item1)).ToArray();\n        }\n\n        protected void ValidateTable<TElement>(IReadOnlyList<TElement> table, ValidationDatabase database, string pkName, Delegate pkSelector, ValidateResult result)\n        {\n            var onceCalled = new System.Runtime.CompilerServices.StrongBox<bool>(false);\n            foreach (var item in table)\n            {\n                if (item is IValidatable<TElement> validatable)\n                {\n                    var validator = new Validator<TElement>(database, item, result, onceCalled, pkName, pkSelector);\n                    validatable.Validate(validator);\n                }\n            }\n        }\n    }\n\n    /// <summary>\n    /// Diagnostic info of MasterMemory's table.\n    /// </summary>\n    public class TableInfo\n    {\n        public string TableName { get; }\n        public int Size { get; }\n        byte[]? binaryData;\n\n        public TableInfo(string tableName, int size, byte[]? rawBinary, int offset)\n        {\n            TableName = tableName;\n            Size = size;\n            if (rawBinary != null)\n            {\n                this.binaryData = new byte[size];\n                Array.Copy(rawBinary, offset, binaryData, 0, size);\n            }\n        }\n\n        public string DumpAsJson()\n        {\n            return DumpAsJson(MessagePackSerializer.DefaultOptions);\n        }\n\n        public string DumpAsJson(MessagePackSerializerOptions options)\n        {\n            if (binaryData == null)\n            {\n                throw new InvalidOperationException(\"DumpAsJson can only call from GetTableInfo(storeTableData = true).\");\n            }\n\n            return MessagePackSerializer.ConvertToJson(binaryData, options.WithCompression(MessagePackCompression.Lz4Block));\n        }\n    }\n}"
  },
  {
    "path": "src/MasterMemory/Meta/Meta.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing System.Collections.Generic;\nusing System.Reflection;\nusing System.Text;\n\nnamespace MasterMemory.Meta\n{\n    public class MetaDatabase\n    {\n        IDictionary<string, MetaTable> tableInfos;\n\n        public MetaDatabase(IDictionary<string, MetaTable> tableInfos)\n        {\n            this.tableInfos = tableInfos;\n        }\n\n        public int Count => tableInfos.Count;\n\n        public IEnumerable<MetaTable> GetTableInfos()\n        {\n            foreach (var item in tableInfos.Values)\n            {\n                yield return item;\n            }\n        }\n\n        public MetaTable? GetTableInfo(string tableName)\n        {\n            return tableInfos.TryGetValue(tableName, out var table)\n                ? table\n                : null;\n        }\n    }\n\n    public class MetaTable\n    {\n        public Type DataType { get; }\n        public Type TableType { get; }\n        public string TableName { get; }\n        public IReadOnlyList<MetaProperty> Properties { get; }\n        public IReadOnlyList<MetaIndex> Indexes { get; }\n\n        public MetaTable(Type dataType, Type tableType, string tableName, IReadOnlyList<MetaProperty> properties, IReadOnlyList<MetaIndex> Indexes)\n        {\n            this.DataType = dataType;\n            this.TableType = tableType;\n            this.TableName = tableName;\n            this.Properties = properties;\n            this.Indexes = Indexes;\n        }\n\n        public override string ToString()\n        {\n            return TableName;\n        }\n    }\n\n    public class MetaProperty\n    {\n        public PropertyInfo PropertyInfo { get; }\n\n        public string Name => PropertyInfo.Name;\n        public string NameLowerCamel => ToCamelCase(PropertyInfo.Name);\n        public string NameSnakeCase => ToSnakeCase(PropertyInfo.Name);\n\n        public MetaProperty(PropertyInfo? propertyInfo)\n        {\n            PropertyInfo = propertyInfo!;\n        }\n\n        public override string ToString()\n        {\n            return Name;\n        }\n\n        /// <summary>\n        /// MyProperty -> myProperty\n        /// </summary>\n        static string ToCamelCase(string s)\n        {\n            if (string.IsNullOrEmpty(s) || char.IsLower(s, 0))\n            {\n                return s;\n            }\n\n            var array = s.ToCharArray();\n            array[0] = char.ToLowerInvariant(array[0]);\n            return new string(array);\n        }\n\n        /// <summary>\n        /// MyProperty -> my_property\n        /// </summary>\n        static string ToSnakeCase(string s)\n        {\n            if (string.IsNullOrEmpty(s)) return s;\n\n            var sb = new StringBuilder();\n            for (int i = 0; i < s.Length; i++)\n            {\n                var c = s[i];\n\n                if (Char.IsUpper(c))\n                {\n                    // first\n                    if (i == 0)\n                    {\n                        sb.Append(char.ToLowerInvariant(c));\n                    }\n                    else if (char.IsUpper(s[i - 1])) // WriteIO => write_io\n                    {\n                        sb.Append(char.ToLowerInvariant(c));\n                    }\n                    else\n                    {\n                        sb.Append(\"_\");\n                        sb.Append(char.ToLowerInvariant(c));\n                    }\n                }\n                else\n                {\n                    sb.Append(c);\n                }\n            }\n\n            return sb.ToString();\n        }\n    }\n\n    public class MetaIndex\n    {\n        public IReadOnlyList<PropertyInfo> IndexProperties { get; }\n        public bool IsPrimaryIndex { get; }\n        public bool IsUnique { get; }\n        public System.Collections.IComparer Comparer { get; }\n        public bool IsReturnRangeValue => IndexProperties.Count != 1;\n\n        public MetaIndex(IReadOnlyList<PropertyInfo> indexProperties, bool isPrimaryIndex, bool isUnique, System.Collections.IComparer comparer)\n        {\n            IndexProperties = indexProperties;\n            IsPrimaryIndex = isPrimaryIndex;\n            IsUnique = isUnique;\n            Comparer = comparer;\n        }\n\n        public override string ToString()\n        {\n            return string.Join(\", \", IndexProperties.Select(x => x.Name));\n        }\n    }\n}"
  },
  {
    "path": "src/MasterMemory/RangeView.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\n\nnamespace MasterMemory\n{\n    public readonly struct RangeView<T> : IEnumerable<T>, IReadOnlyList<T>, IList<T>\n    {\n        public static RangeView<T> Empty => new RangeView<T>(null, -1, -1, false);\n\n        readonly T[]? orderedData;\n        readonly int left;\n        readonly int right;\n        readonly bool ascendant;\n        readonly bool hasValue;\n\n        public int Count => (!hasValue) ? 0 : (right - left) + 1;\n        public T First => this[0];\n        public T Last => this[Count - 1];\n\n        public RangeView<T> Reverse => new RangeView<T>(orderedData, left, right, !ascendant);\n\n        internal int FirstIndex => ascendant ? left : right;\n        internal int LastIndex => ascendant ? right : left;\n\n        bool ICollection<T>.IsReadOnly => true;\n\n        public T this[int index]\n        {\n            get\n            {\n                if (!hasValue) throw new ArgumentOutOfRangeException(\"view is empty\");\n                if (index < 0) throw new ArgumentOutOfRangeException(\"index < 0\");\n                if (Count <= index) throw new ArgumentOutOfRangeException(\"count <= index\");\n\n                if (ascendant)\n                {\n                    return orderedData![left + index];\n                }\n                else\n                {\n                    return orderedData![right - index];\n                }\n            }\n        }\n\n        public RangeView(T[]? orderedData, int left, int right, bool ascendant)\n        {\n            this.hasValue = (orderedData != null) && (orderedData.Length != 0) && (left <= right); // same index is length = 1            this.orderedData = orderedData;\n            this.orderedData = orderedData;\n            this.left = left;\n            this.right = right;\n            this.ascendant = ascendant;\n        }\n\n        public IEnumerator<T> GetEnumerator()\n        {\n            var count = Count;\n            for (int i = 0; i < count; i++)\n            {\n                yield return this[i];\n            }\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n\n        public bool Any()\n        {\n            return Count != 0;\n        }\n\n        public int IndexOf(T item)\n        {\n            var i = 0;\n            foreach (var v in this)\n            {\n                if (EqualityComparer<T>.Default.Equals(v, item))\n                {\n                    return i;\n                }\n                i++;\n            }\n            return -1;\n        }\n\n        public bool Contains(T item)\n        {\n            var count = Count;\n            for (int i = 0; i < count; i++)\n            {\n                var v = this[i];\n                if (EqualityComparer<T>.Default.Equals(v, item))\n                {\n                    return true;\n                }\n            }\n            return false;\n        }\n\n        public void CopyTo(T[] array, int arrayIndex)\n        {\n            var count = Count;\n            Array.Copy(orderedData, left, array, arrayIndex, count);\n            if (!ascendant)\n            {\n                Array.Reverse(array, arrayIndex, count);\n            }\n        }\n\n        T IList<T>.this[int index]\n        {\n            get\n            {\n                return this[index];\n            }\n            set\n            {\n                throw new NotImplementedException();\n            }\n        }\n\n        void IList<T>.Insert(int index, T item)\n        {\n            throw new NotSupportedException();\n        }\n\n        void IList<T>.RemoveAt(int index)\n        {\n            throw new NotSupportedException();\n        }\n\n        void ICollection<T>.Add(T item)\n        {\n            throw new NotSupportedException();\n        }\n\n        void ICollection<T>.Clear()\n        {\n            throw new NotSupportedException();\n        }\n\n        bool ICollection<T>.Remove(T item)\n        {\n            throw new NotSupportedException();\n        }\n    }\n}"
  },
  {
    "path": "src/MasterMemory/TableBase.cs",
    "content": "﻿using MasterMemory.Internal;\nusing MasterMemory.Validation;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\n\nnamespace MasterMemory\n{\n    public abstract class TableBase<TElement>\n    {\n        protected readonly TElement[] data;\n\n        // Common Properties\n        public int Count => data.Length;\n        public RangeView<TElement> All => new RangeView<TElement>(data, 0, data.Length - 1, true);\n        public RangeView<TElement> AllReverse => new RangeView<TElement>(data, 0, data.Length - 1, false);\n        public TElement[] GetRawDataUnsafe() => data;\n\n        public TableBase(TElement[] sortedData)\n        {\n            this.data = sortedData;\n        }\n\n        // Validate\n\n        static protected void ValidateUniqueCore<TKey>(TElement[] indexArray, Func<TElement, TKey> keySelector, string message, ValidateResult resultSet)\n        {\n            var set = new HashSet<TKey>();\n            foreach (var item in indexArray)\n            {\n                var v = keySelector(item);\n                if (!set.Add(v))\n                {\n                    resultSet.AddFail(typeof(TElement), \"Unique failed: \" + message + \", value = \" + v, item!);\n                }\n            }\n        }\n\n        // Util\n\n        protected TElement[] CloneAndSortBy<TKey>(Func<TElement, TKey> indexSelector, IComparer<TKey> comparer)\n        {\n            var array = new TElement[data.Length];\n            var sortSource = new TKey[data.Length];\n            for (int i = 0; i < data.Length; i++)\n            {\n                array[i] = data[i];\n                sortSource[i] = indexSelector(data[i]);\n            }\n\n            Array.Sort(sortSource, array, 0, array.Length, comparer);\n            return array;\n        }\n\n        static protected TElement ThrowKeyNotFound<TKey>(TKey key)\n        {\n            throw new KeyNotFoundException(\"DataType: \" + typeof(TElement).FullName + \", Key: \" + key?.ToString());\n        }\n\n        // Unique\n\n        static protected TElement FindUniqueCore<TKey>(TElement[] indexArray, Func<TElement, TKey> keySelector, IComparer<TKey> comparer, TKey key, bool throwIfNotFound = true)\n        {\n            var index = BinarySearch.FindFirst(indexArray, key, keySelector, comparer);\n            if (index != -1)\n            {\n                return indexArray[index];\n            }\n            else\n            {\n                if (throwIfNotFound)\n                {\n                    ThrowKeyNotFound(key);\n                }\n                return default!;\n            }\n        }\n\n        // Optimize for IntKey\n        static protected TElement FindUniqueCoreInt(TElement[] indexArray, Func<TElement, int> keySelector, IComparer<int> _, int key, bool throwIfNotFound = true)\n        {\n            var index = BinarySearch.FindFirstIntKey(indexArray, key, keySelector);\n            if (index != -1)\n            {\n                return indexArray[index];\n            }\n            else\n            {\n                if (throwIfNotFound)\n                {\n                    ThrowKeyNotFound(key);\n                }\n                return default!;\n            }\n        }\n\n        static protected bool TryFindUniqueCore<TKey>(TElement[] indexArray, Func<TElement, TKey> keySelector, IComparer<TKey> comparer, TKey key, out TElement result)\n        {\n            var index = BinarySearch.FindFirst(indexArray, key, keySelector, comparer);\n            if (index != -1)\n            {\n                result = indexArray[index];\n                return true;\n            }\n            else\n            {\n                result = default!;\n                return false;\n            }\n        }\n\n        static protected bool TryFindUniqueCoreInt(TElement[] indexArray, Func<TElement, int> keySelector, IComparer<int> _, int key, out TElement result)\n        {\n            var index = BinarySearch.FindFirstIntKey(indexArray, key, keySelector);\n            if (index != -1)\n            {\n                result = indexArray[index];\n                return true;\n            }\n            else\n            {\n                result = default!;\n                return false;\n            }\n        }\n\n        static protected TElement? FindUniqueClosestCore<TKey>(TElement[] indexArray, Func<TElement, TKey> keySelector, IComparer<TKey> comparer, TKey key, bool selectLower)\n        {\n            var index = BinarySearch.FindClosest(indexArray, 0, indexArray.Length, key, keySelector, comparer, selectLower);\n            return (index != -1) ? indexArray[index] : default(TElement);\n        }\n\n        static protected RangeView<TElement> FindUniqueRangeCore<TKey>(TElement[] indexArray, Func<TElement, TKey> keySelector, IComparer<TKey> comparer, TKey min, TKey max, bool ascendant)\n        {\n            var lo = BinarySearch.FindClosest(indexArray, 0, indexArray.Length, min, keySelector, comparer, false);\n            var hi = BinarySearch.FindClosest(indexArray, 0, indexArray.Length, max, keySelector, comparer, true);\n\n            if ( lo == -1 ) lo = 0;\n            if ( hi == indexArray.Length ) hi -= 1;\n\n            return new RangeView<TElement>(indexArray, lo, hi, ascendant);\n        }\n\n        // Many\n\n        static protected RangeView<TElement> FindManyCore<TKey>(TElement[] indexKeys, Func<TElement, TKey> keySelector, IComparer<TKey> comparer, TKey key)\n        {\n            var lo = BinarySearch.LowerBound(indexKeys, 0, indexKeys.Length, key, keySelector, comparer);\n            if (lo == -1) return RangeView<TElement>.Empty;\n\n            var hi = BinarySearch.UpperBound(indexKeys, 0, indexKeys.Length, key, keySelector, comparer);\n            if (hi == -1) return RangeView<TElement>.Empty;\n\n            return new RangeView<TElement>(indexKeys, lo, hi, true);\n        }\n\n        static protected RangeView<TElement> FindManyClosestCore<TKey>(TElement[] indexArray, Func<TElement, TKey> keySelector, IComparer<TKey> comparer, TKey key, bool selectLower)\n        {\n            var closest = BinarySearch.FindClosest(indexArray, 0, indexArray.Length, key, keySelector, comparer, selectLower);\n\n            if ((closest == -1) || ( closest >= indexArray.Length ))\n                return RangeView<TElement>.Empty;\n\n            return FindManyCore(indexArray, keySelector, comparer, keySelector(indexArray[closest]));\n        }\n\n        static protected RangeView<TElement> FindManyRangeCore<TKey>(TElement[] indexArray, Func<TElement, TKey> keySelector, IComparer<TKey> comparer, TKey min, TKey max, bool ascendant)\n        {\n            //... Empty set when min > max\n            //... Alternatively, could treat this as between and swap min and max.\n\n            if ( Comparer<TKey>.Default.Compare( min, max ) > 0 )\n               return RangeView<TElement>.Empty;\n\n            //... want lo to be the lowest  index of the values >= than min.\n            //... lo should be in the range [0,arraylength]\n\n            var lo = BinarySearch.LowerBoundClosest(indexArray, 0, indexArray.Length, min, keySelector, comparer );\n\n            //... want hi to be the highest index of the values <= than max\n            //... hi should be in the range [-1,arraylength-1]\n\n            var hi = BinarySearch.UpperBoundClosest(indexArray, 0, indexArray.Length, max, keySelector, comparer );\n\n            Debug.Assert( lo >= 0 );\n            Debug.Assert( hi < indexArray.Length );\n\n            if ( hi < lo )\n               return RangeView<TElement>.Empty;\n\n            return new RangeView<TElement>(indexArray, lo, hi, ascendant);\n        }\n    }\n}\n"
  },
  {
    "path": "src/MasterMemory/Validation/ExpressionDumper.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing System.Collections.Generic;\nusing System.Linq.Expressions;\nusing System.Reflection;\nusing System.Text;\n\nnamespace MasterMemory.Validation\n{\n    internal class ExpressionDumper<T> : ExpressionVisitor\n    {\n        ParameterExpression param;\n        T target;\n\n        public Dictionary<string, object> Members { get; private set; }\n\n        public ExpressionDumper(T target, ParameterExpression param)\n        {\n            this.target = target;\n            this.param = param;\n            this.Members = new Dictionary<string, object>();\n        }\n\n        protected override System.Linq.Expressions.Expression VisitMember(MemberExpression node)\n        {\n            if (node.Expression == param && !Members.ContainsKey(node.Member.Name))\n            {\n                var accessor = new ReflectAccessor(target, node.Member.Name);\n                Members.Add(node.Member.Name, accessor.GetValue());\n            }\n\n            return base.VisitMember(node);\n        }\n\n        public static string DumpMemberValues(T item, Expression<Func<T, bool>> predicate)\n        {\n            var dumper = new ExpressionDumper<T>(item, predicate.Parameters.Single());\n            return dumper.VisitAndFormat(predicate);\n        }\n\n        public string VisitAndFormat(Expression expression)\n        {\n            Visit(expression);\n            return string.Join(\", \", Members.Select(kvp => kvp.Key + \" = \" + kvp.Value));\n        }\n\n        private class ReflectAccessor\n        {\n            public Func<object> GetValue { get; private set; }\n            public Action<object> SetValue { get; private set; }\n\n            public ReflectAccessor(T target, string name)\n            {\n                var field = typeof(T).GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);\n                if (field != null)\n                {\n                    GetValue = () => field.GetValue(target);\n                    SetValue = value => field.SetValue(target, value);\n                    return;\n                }\n\n                var prop = typeof(T).GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);\n                if (prop != null)\n                {\n                    GetValue = () => prop.GetValue(target, null);\n                    SetValue = value => prop.SetValue(target, value, null);\n                    return;\n                }\n\n                throw new ArgumentException(string.Format(\"\\\"{0}\\\" not found : Type <{1}>\", name, typeof(T).Name));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/MasterMemory/Validation/ExpressionParameterNameModifier.cs",
    "content": "﻿using System;\nusing System.Linq.Expressions;\n\nnamespace MasterMemory.Validation\n{\n    public class ExpressionParameterNameModifier : ExpressionVisitor\n    {\n        readonly ParameterExpression modifyTarget;\n        readonly ParameterExpression replaceExpression;\n\n        public ExpressionParameterNameModifier(ParameterExpression modifyTarget, ParameterExpression replaceExpression)\n        {\n            this.modifyTarget = modifyTarget;\n            this.replaceExpression = replaceExpression;\n        }\n\n        protected override Expression VisitParameter(ParameterExpression node)\n        {\n            if (node == modifyTarget)\n            {\n                return replaceExpression;\n            }\n\n            return base.VisitParameter(node);\n        }\n    }\n\n    public static class ExpressionParameterNameModifyExtensions\n    {\n        public static string ToThisBodyString<T>(this Expression<Func<T, bool>> predicate)\n        {\n            var newNameParameter = Expression.Parameter(typeof(T), \"this\");\n            var newExpression = new ExpressionParameterNameModifier(predicate.Parameters[0], newNameParameter).Visit(predicate);\n            return (newExpression as Expression<Func<T, bool>>)!.Body.ToString();\n        }\n\n        public static string ToSpaceBodyString<T, TProperty>(this Expression<Func<T, TProperty>> selector)\n        {\n            var newNameParameter = Expression.Parameter(typeof(T), \" \");\n            var newExpression = new ExpressionParameterNameModifier(selector.Parameters[0], newNameParameter).Visit(selector);\n            return (newExpression as Expression<Func<T, TProperty>>)!.Body.ToString();\n        }\n\n        public static string ToNameBodyString<T, TProperty>(this Expression<Func<T, TProperty>> selector, string newName)\n        {\n            var newNameParameter = Expression.Parameter(typeof(T), newName);\n            var newExpression = new ExpressionParameterNameModifier(selector.Parameters[0], newNameParameter).Visit(selector);\n            return (newExpression as Expression<Func<T, TProperty>>)!.Body.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "src/MasterMemory/Validation/ITableUniqueValidate.cs",
    "content": "﻿namespace MasterMemory.Validation\n{\n    public interface ITableUniqueValidate\n    {\n        void ValidateUnique(ValidateResult resultSet);\n    }\n}\n"
  },
  {
    "path": "src/MasterMemory/Validation/ReferenceSet.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing System.Collections.Generic;\nusing System.Linq.Expressions;\n\nnamespace MasterMemory.Validation\n{\n    public class ReferenceSet<TElement, TReference>\n    {\n        readonly TElement item;\n        readonly IReadOnlyList<TReference> referenceTable;\n        readonly ValidateResult resultSet;\n        readonly string pkName;\n        readonly Delegate pkSelector;\n\n        public IReadOnlyList<TReference> TableData => referenceTable;\n\n        public ReferenceSet(TElement item, IReadOnlyList<TReference> referenceTable, ValidateResult resultSet, string pkName, Delegate pkSelector)\n        {\n            this.item = item;\n            this.referenceTable = referenceTable;\n            this.resultSet = resultSet;\n            this.pkName = pkName;\n            this.pkSelector = pkSelector;\n        }\n\n        public void Exists<TProperty>(Expression<Func<TElement, TProperty>> elementSelector, Expression<Func<TReference, TProperty>> referenceElementSelector)\n        {\n            Exists(elementSelector, referenceElementSelector, EqualityComparer<TProperty>.Default);\n        }\n\n        public void Exists<TProperty>(Expression<Func<TElement, TProperty>> elementSelector, Expression<Func<TReference, TProperty>> referenceElementSelector, EqualityComparer<TProperty> equalityComparer)\n        {\n            var f1 = elementSelector.Compile(true);\n            var f2 = referenceElementSelector.Compile(true);\n\n            var compareBase = f1(item);\n            foreach (var refItem in referenceTable)\n            {\n                if (equalityComparer.Equals(compareBase, f2(refItem)))\n                {\n                    return;\n                }\n            }\n\n            // not found, assert.\n            var from = elementSelector.ToNameBodyString(typeof(TElement).Name);\n            var to = referenceElementSelector.ToNameBodyString(typeof(TReference).Name);\n            resultSet.AddFail(typeof(TElement), \"Exists failed: \" + from + \" -> \" + to + \", value = \" + compareBase + \", \" + BuildPkMessage(), item!);\n        }\n\n        string BuildPkMessage()\n        {\n            var pk = pkSelector.DynamicInvoke(item).ToString();\n            return $\"PK({pkName}) = {pk}\";\n        }\n    }\n}"
  },
  {
    "path": "src/MasterMemory/Validation/ValidatableSet.Sequential.cs",
    "content": "﻿ \nusing System;\nusing System.Linq;\nusing System.Linq.Expressions;\n\nnamespace MasterMemory.Validation\n{\n    public partial class ValidatableSet<TElement>\n    {\n        public void Sequential(Expression<Func<TElement, SByte>> selector, bool distinct = false)\n        {\n            var f = selector.Compile(true);\n            SequentialCore(f, () => selector.ToSpaceBodyString(), distinct);\n        }\n\n        public void Sequential(Func<TElement, SByte> selector, string message, bool distinct = false)\n        {\n            SequentialCore(selector, () => \" \" + message, distinct);\n        }\n\n        void SequentialCore(Func<TElement, SByte> selector, Func<string> message, bool distinct)\n        {\n            if (tableData.Count == 0) return;\n            var data = tableData.OrderBy(selector).ToArray();\n\n            var prev = selector(data[0]);\n            for (int i = 1; i < data.Length; i++)\n            {\n                var curr = selector(data[i]);\n                if (distinct)\n                {\n                    if (prev == curr) continue;\n                }\n\n                if ((prev + 1) != curr)\n                {\n                    resultSet.AddFail(typeof(TElement), \"Sequential failed:\" + message() + \", value = \" + (prev, curr) + \", \" + BuildPkMessage(data[i]), data[i]!);\n                }\n\n                prev = curr;\n            }\n        }\n\n        public void Sequential(Expression<Func<TElement, Int16>> selector, bool distinct = false)\n        {\n            var f = selector.Compile(true);\n            SequentialCore(f, () => selector.ToSpaceBodyString(), distinct);\n        }\n\n        public void Sequential(Func<TElement, Int16> selector, string message, bool distinct = false)\n        {\n            SequentialCore(selector, () => \" \" + message, distinct);\n        }\n\n        void SequentialCore(Func<TElement, Int16> selector, Func<string> message, bool distinct)\n        {\n            if (tableData.Count == 0) return;\n            var data = tableData.OrderBy(selector).ToArray();\n\n            var prev = selector(data[0]);\n            for (int i = 1; i < data.Length; i++)\n            {\n                var curr = selector(data[i]);\n                if (distinct)\n                {\n                    if (prev == curr) continue;\n                }\n\n                if ((prev + 1) != curr)\n                {\n                    resultSet.AddFail(typeof(TElement), \"Sequential failed:\" + message() + \", value = \" + (prev, curr) + \", \" + BuildPkMessage(data[i]), data[i]!);\n                }\n\n                prev = curr;\n            }\n        }\n\n        public void Sequential(Expression<Func<TElement, Int32>> selector, bool distinct = false)\n        {\n            var f = selector.Compile(true);\n            SequentialCore(f, () => selector.ToSpaceBodyString(), distinct);\n        }\n\n        public void Sequential(Func<TElement, Int32> selector, string message, bool distinct = false)\n        {\n            SequentialCore(selector, () => \" \" + message, distinct);\n        }\n\n        void SequentialCore(Func<TElement, Int32> selector, Func<string> message, bool distinct)\n        {\n            if (tableData.Count == 0) return;\n            var data = tableData.OrderBy(selector).ToArray();\n\n            var prev = selector(data[0]);\n            for (int i = 1; i < data.Length; i++)\n            {\n                var curr = selector(data[i]);\n                if (distinct)\n                {\n                    if (prev == curr) continue;\n                }\n\n                if ((prev + 1) != curr)\n                {\n                    resultSet.AddFail(typeof(TElement), \"Sequential failed:\" + message() + \", value = \" + (prev, curr) + \", \" + BuildPkMessage(data[i]), data[i]!);\n                }\n\n                prev = curr;\n            }\n        }\n\n        public void Sequential(Expression<Func<TElement, Int64>> selector, bool distinct = false)\n        {\n            var f = selector.Compile(true);\n            SequentialCore(f, () => selector.ToSpaceBodyString(), distinct);\n        }\n\n        public void Sequential(Func<TElement, Int64> selector, string message, bool distinct = false)\n        {\n            SequentialCore(selector, () => \" \" + message, distinct);\n        }\n\n        void SequentialCore(Func<TElement, Int64> selector, Func<string> message, bool distinct)\n        {\n            if (tableData.Count == 0) return;\n            var data = tableData.OrderBy(selector).ToArray();\n\n            var prev = selector(data[0]);\n            for (int i = 1; i < data.Length; i++)\n            {\n                var curr = selector(data[i]);\n                if (distinct)\n                {\n                    if (prev == curr) continue;\n                }\n\n                if ((prev + 1) != curr)\n                {\n                    resultSet.AddFail(typeof(TElement), \"Sequential failed:\" + message() + \", value = \" + (prev, curr) + \", \" + BuildPkMessage(data[i]), data[i]!);\n                }\n\n                prev = curr;\n            }\n        }\n\n        public void Sequential(Expression<Func<TElement, Byte>> selector, bool distinct = false)\n        {\n            var f = selector.Compile(true);\n            SequentialCore(f, () => selector.ToSpaceBodyString(), distinct);\n        }\n\n        public void Sequential(Func<TElement, Byte> selector, string message, bool distinct = false)\n        {\n            SequentialCore(selector, () => \" \" + message, distinct);\n        }\n\n        void SequentialCore(Func<TElement, Byte> selector, Func<string> message, bool distinct)\n        {\n            if (tableData.Count == 0) return;\n            var data = tableData.OrderBy(selector).ToArray();\n\n            var prev = selector(data[0]);\n            for (int i = 1; i < data.Length; i++)\n            {\n                var curr = selector(data[i]);\n                if (distinct)\n                {\n                    if (prev == curr) continue;\n                }\n\n                if ((prev + 1) != curr)\n                {\n                    resultSet.AddFail(typeof(TElement), \"Sequential failed:\" + message() + \", value = \" + (prev, curr) + \", \" + BuildPkMessage(data[i]), data[i]!);\n                }\n\n                prev = curr;\n            }\n        }\n\n        public void Sequential(Expression<Func<TElement, UInt16>> selector, bool distinct = false)\n        {\n            var f = selector.Compile(true);\n            SequentialCore(f, () => selector.ToSpaceBodyString(), distinct);\n        }\n\n        public void Sequential(Func<TElement, UInt16> selector, string message, bool distinct = false)\n        {\n            SequentialCore(selector, () => \" \" + message, distinct);\n        }\n\n        void SequentialCore(Func<TElement, UInt16> selector, Func<string> message, bool distinct)\n        {\n            if (tableData.Count == 0) return;\n            var data = tableData.OrderBy(selector).ToArray();\n\n            var prev = selector(data[0]);\n            for (int i = 1; i < data.Length; i++)\n            {\n                var curr = selector(data[i]);\n                if (distinct)\n                {\n                    if (prev == curr) continue;\n                }\n\n                if ((prev + 1) != curr)\n                {\n                    resultSet.AddFail(typeof(TElement), \"Sequential failed:\" + message() + \", value = \" + (prev, curr) + \", \" + BuildPkMessage(data[i]), data[i]!);\n                }\n\n                prev = curr;\n            }\n        }\n\n        public void Sequential(Expression<Func<TElement, UInt32>> selector, bool distinct = false)\n        {\n            var f = selector.Compile(true);\n            SequentialCore(f, () => selector.ToSpaceBodyString(), distinct);\n        }\n\n        public void Sequential(Func<TElement, UInt32> selector, string message, bool distinct = false)\n        {\n            SequentialCore(selector, () => \" \" + message, distinct);\n        }\n\n        void SequentialCore(Func<TElement, UInt32> selector, Func<string> message, bool distinct)\n        {\n            if (tableData.Count == 0) return;\n            var data = tableData.OrderBy(selector).ToArray();\n\n            var prev = selector(data[0]);\n            for (int i = 1; i < data.Length; i++)\n            {\n                var curr = selector(data[i]);\n                if (distinct)\n                {\n                    if (prev == curr) continue;\n                }\n\n                if ((prev + 1) != curr)\n                {\n                    resultSet.AddFail(typeof(TElement), \"Sequential failed:\" + message() + \", value = \" + (prev, curr) + \", \" + BuildPkMessage(data[i]), data[i]!);\n                }\n\n                prev = curr;\n            }\n        }\n\n        public void Sequential(Expression<Func<TElement, UInt64>> selector, bool distinct = false)\n        {\n            var f = selector.Compile(true);\n            SequentialCore(f, () => selector.ToSpaceBodyString(), distinct);\n        }\n\n        public void Sequential(Func<TElement, UInt64> selector, string message, bool distinct = false)\n        {\n            SequentialCore(selector, () => \" \" + message, distinct);\n        }\n\n        void SequentialCore(Func<TElement, UInt64> selector, Func<string> message, bool distinct)\n        {\n            if (tableData.Count == 0) return;\n            var data = tableData.OrderBy(selector).ToArray();\n\n            var prev = selector(data[0]);\n            for (int i = 1; i < data.Length; i++)\n            {\n                var curr = selector(data[i]);\n                if (distinct)\n                {\n                    if (prev == curr) continue;\n                }\n\n                if ((prev + 1) != curr)\n                {\n                    resultSet.AddFail(typeof(TElement), \"Sequential failed:\" + message() + \", value = \" + (prev, curr) + \", \" + BuildPkMessage(data[i]), data[i]!);\n                }\n\n                prev = curr;\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/MasterMemory/Validation/ValidatableSet.Sequential.tt",
    "content": "﻿<#@ template debug=\"true\" hostSpecific=\"false\" #>\n<#@ output extension=\".cs\" #>\n<#@ Assembly Name=\"System.Core\" #>\n<#@ import namespace=\"System\" #>\n<#@ import namespace=\"System.IO\" #>\n<#@ import namespace=\"System.Diagnostics\" #>\n<#@ import namespace=\"System.Linq\" #>\n<#@ import namespace=\"System.Collections\" #>\n<#@ import namespace=\"System.Collections.Generic\" #> \n<#\n    var targetTypes = new[]\n    {\n        typeof(sbyte),\n        typeof(short),\n        typeof(int),\n        typeof(long),\n        typeof(byte),\n        typeof(ushort),\n        typeof(uint),\n        typeof(ulong),\n    };\n#>\nusing System;\nusing System.Linq;\nusing System.Linq.Expressions;\n\nnamespace MasterMemory.Validation\n{\n    public partial class ValidatableSet<TElement>\n    {\n<# foreach(var t in targetTypes) { #>\n        public void Sequential(Expression<Func<TElement, <#= t.Name #>>> selector, bool distinct = false)\n        {\n            var f = selector.Compile(true);\n            SequentialCore(f, () => selector.ToSpaceBodyString(), distinct);\n        }\n\n        public void Sequential(Func<TElement, <#= t.Name #>> selector, string message, bool distinct = false)\n        {\n            SequentialCore(selector, () => \" \" + message, distinct);\n        }\n\n        void SequentialCore(Func<TElement, <#= t.Name #>> selector, Func<string> message, bool distinct)\n        {\n            if (tableData.Count == 0) return;\n            var data = tableData.OrderBy(selector).ToArray();\n\n            var prev = selector(data[0]);\n            for (int i = 1; i < data.Length; i++)\n            {\n                var curr = selector(data[i]);\n                if (distinct)\n                {\n                    if (prev == curr) continue;\n                }\n\n                if ((prev + 1) != curr)\n                {\n                    resultSet.AddFail(typeof(TElement), \"Sequential failed:\" + message() + \", value = \" + (prev, curr) + \", \" + BuildPkMessage(data[i]), data[i]);\n                }\n\n                prev = curr;\n            }\n        }\n\n<# } #>\n    }\n}\n"
  },
  {
    "path": "src/MasterMemory/Validation/ValidatableSet.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Linq.Expressions;\n\nnamespace MasterMemory.Validation\n{\n    public partial class ValidatableSet<TElement>\n    {\n        readonly IReadOnlyList<TElement> tableData;\n        readonly ValidateResult resultSet;\n        readonly string pkName;\n        readonly Delegate pkSelector;\n\n        public ValidatableSet(IReadOnlyList<TElement> tableData, ValidateResult resultSet, string pkName, Delegate pkSelector)\n        {\n            this.tableData = tableData;\n            this.resultSet = resultSet;\n            this.pkName = pkName;\n            this.pkSelector = pkSelector;\n        }\n\n        public IReadOnlyList<TElement> TableData => tableData;\n\n        public void Unique<TProperty>(Expression<Func<TElement, TProperty>> selector)\n        {\n            Unique(selector, EqualityComparer<TProperty>.Default);\n        }\n\n        public void Unique<TProperty>(Expression<Func<TElement, TProperty>> selector, IEqualityComparer<TProperty> equalityComparer)\n        {\n            var f = selector.Compile(true);\n\n            var set = new HashSet<TProperty>(equalityComparer);\n            foreach (var item in tableData)\n            {\n                var v = f(item);\n                if (!set.Add(v))\n                {\n                    resultSet.AddFail(typeof(TElement), \"Unique failed:\" + selector.ToSpaceBodyString() + \", value = \" + v + \", \" + BuildPkMessage(item), item!);\n                }\n            }\n        }\n\n        public void Unique<TProperty>(Func<TElement, TProperty> selector, string message)\n        {\n            Unique(selector, EqualityComparer<TProperty>.Default, message);\n        }\n\n        public void Unique<TProperty>(Func<TElement, TProperty> selector, IEqualityComparer<TProperty> equalityComparer, string message)\n        {\n            var set = new HashSet<TProperty>(equalityComparer);\n            foreach (var item in tableData)\n            {\n                var v = selector(item);\n                if (!set.Add(v))\n                {\n                    resultSet.AddFail(typeof(TElement), \"Unique failed: \" + message + \", value = \" + v + \", \" + BuildPkMessage(item), item!);\n                }\n            }\n        }\n\n        public ValidatableSet<TElement> Where(Func<TElement, bool> predicate)\n        {\n            return new ValidatableSet<TElement>(tableData.Where(predicate).ToArray(), resultSet, pkName, pkSelector);\n        }\n\n        string BuildPkMessage(TElement item)\n        {\n            var pk = pkSelector.DynamicInvoke(item).ToString();\n            return $\"PK({pkName}) = {pk}\";\n        }\n    }\n}\n"
  },
  {
    "path": "src/MasterMemory/Validation/ValidateResult.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace MasterMemory.Validation\n{\n    public class ValidateResult\n    {\n        List<FaildItem> result = new List<FaildItem>();\n\n        public bool IsValidationFailed => result.Count != 0;\n\n        public IReadOnlyList<FaildItem> FailedResults => result;\n\n        public string FormatFailedResults()\n        {\n            var sb = new StringBuilder();\n            foreach (var item in result)\n            {\n                sb.AppendLine(item.Type.FullName + \" - \" + item.Message);\n            }\n            return sb.ToString();\n        }\n\n        internal void AddFail(Type type, string message, object data)\n        {\n            result.Add(new FaildItem(type, message, data));\n        }\n    }\n\n    public readonly struct FaildItem\n    {\n        public FaildItem(Type type, string message, object data)\n        {\n            Type = type;\n            Message = message;\n            Data = data;\n        }\n\n        public Type Type { get; }\n        public string Message { get; }\n        public object Data { get; }\n    }\n}\n"
  },
  {
    "path": "src/MasterMemory/Validation/ValidationDatabase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nnamespace MasterMemory.Validation\n{\n    public class ValidationDatabase\n    {\n        // {Type, IReadOnlyList<T> }\n        readonly Dictionary<Type, object> dataTables = new Dictionary<Type, object>();\n\n        public ValidationDatabase(IEnumerable<object> tables)\n        {\n            foreach (var table in tables)\n            {\n                // TableBase<T>\n                var baseType = table.GetType().BaseType;\n\n                // RangeView<T>\n                var rangeViewAll = baseType.GetProperty(\"All\").GetGetMethod().Invoke(table, null);\n\n                var elementType = baseType.GetGenericArguments()[0];\n                dataTables.Add(elementType, rangeViewAll);\n            }\n        }\n\n        internal IReadOnlyList<T> GetTable<T>()\n        {\n            if (!dataTables.TryGetValue(typeof(T), out var table))\n            {\n                throw new InvalidOperationException(\"Can not create validator in \" + typeof(T).FullName);\n            }\n            var data = table as IReadOnlyList<T>;\n            return data!;\n        }\n    }\n}\n"
  },
  {
    "path": "src/MasterMemory/Validation/Validator.cs",
    "content": "﻿using System;\nusing System.Linq.Expressions;\nusing System.Runtime.CompilerServices;\n\nnamespace MasterMemory.Validation\n{\n    internal class Validator<T> : IValidator<T>\n    {\n        readonly ValidationDatabase database;\n        readonly T item;\n        readonly ValidateResult resultSet;\n        readonly StrongBox<bool> onceCalled;\n        readonly string pkName;\n        readonly Delegate pkSelector;\n\n        public Validator(ValidationDatabase database, T item, ValidateResult resultSet, StrongBox<bool> onceCalled, string pkName, Delegate pkSelector)\n        {\n            this.database = database;\n            this.item = item;\n            this.resultSet = resultSet;\n            this.onceCalled = onceCalled;\n            this.pkName = pkName;\n            this.pkSelector = pkSelector;\n        }\n\n        public bool CallOnce()\n        {\n            if (!onceCalled.Value)\n            {\n                onceCalled.Value = true;\n                return true;\n            }\n\n            return false;\n        }\n\n        public ValidatableSet<T> GetTableSet()\n        {\n            return new ValidatableSet<T>(database.GetTable<T>(), resultSet, pkName, pkSelector);\n        }\n\n        public ReferenceSet<T, TRef> GetReferenceSet<TRef>()\n        {\n            var table = database.GetTable<TRef>();\n            return new ReferenceSet<T, TRef>(item, table, resultSet, pkName, pkSelector);\n        }\n\n        public void Validate(Expression<Func<T, bool>> predicate)\n        {\n            if (!predicate.Compile(true).Invoke(item))\n            {\n                var memberValues = ExpressionDumper<T>.DumpMemberValues(item, predicate);\n                var message = string.Format($\"{predicate.ToThisBodyString()}, {memberValues}, {BuildPkMessage()}\");\n                resultSet.AddFail(typeof(T), \"Validate failed: \" + message, item!);\n            }\n        }\n\n        public void Validate(Func<T, bool> predicate, string message)\n        {\n            if (!predicate(item))\n            {\n                resultSet.AddFail(typeof(T), \"Validate failed: \" + message + \", \" + BuildPkMessage(), item!);\n            }\n        }\n\n        public void ValidateAction(Expression<Func<bool>> predicate)\n        {\n            if (!predicate.Compile(true).Invoke())\n            {\n                var expr = predicate.Body.ToString();\n                resultSet.AddFail(typeof(T), \"ValidateAction failed: \" + expr + \", \" + BuildPkMessage(), item!);\n            }\n        }\n\n        public void ValidateAction(Func<bool> predicate, string message)\n        {\n            if (!predicate())\n            {\n                resultSet.AddFail(typeof(T), \"ValidateAction failed: \" + message + \", \" + BuildPkMessage(), item!);\n            }\n        }\n\n        public void Fail(string message)\n        {\n            resultSet.AddFail(typeof(T), message + \", \" + BuildPkMessage(), item!);\n        }\n\n        string BuildPkMessage()\n        {\n            var pk = pkSelector.DynamicInvoke(item).ToString();\n            return $\"PK({pkName}) = {pk}\";\n        }\n    }\n}"
  },
  {
    "path": "src/MasterMemory/_InternalVisibleTo.cs",
    "content": "﻿using System.Runtime.CompilerServices;\n\n[assembly: InternalsVisibleTo(\"MasterMemory.Tests\")]"
  },
  {
    "path": "src/MasterMemory/_MessagePackResolver.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace MasterMemory;\n\n[MessagePack.GeneratedMessagePackResolver]\ninternal partial class _MessagePackResolver\n{\n}"
  },
  {
    "path": "src/MasterMemory.Annotations/Attributes.cs",
    "content": "﻿using System;\n\nnamespace MasterMemory\n{\n    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]\n    public class MemoryTableAttribute : Attribute\n    {\n        public string TableName { get; }\n\n        public MemoryTableAttribute(string tableName)\n        {\n            this.TableName = tableName;\n        }\n    }\n\n    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]\n    public class PrimaryKeyAttribute : Attribute\n    {\n        public int KeyOrder { get; }\n\n        public PrimaryKeyAttribute(int keyOrder = 0)\n        {\n            this.KeyOrder = keyOrder;\n        }\n    }\n\n    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]\n    public class SecondaryKeyAttribute : Attribute\n    {\n        public int IndexNo { get; }\n        public int KeyOrder { get; }\n\n        public SecondaryKeyAttribute(int indexNo, int keyOrder = 0)\n        {\n            this.IndexNo = indexNo;\n            this.KeyOrder = keyOrder;\n        }\n    }\n\n    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]\n    public class NonUniqueAttribute : Attribute\n    {\n\n    }\n\n    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]\n    public class StringComparisonOptionAttribute : Attribute\n    {\n        public StringComparison StringComparison { get; }\n\n        public StringComparisonOptionAttribute(StringComparison stringComparison)\n        {\n            this.StringComparison = stringComparison;\n        }\n    }\n}"
  },
  {
    "path": "src/MasterMemory.Annotations/MasterMemory.Annotations.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFramework>netstandard2.0</TargetFramework>\n\t\t<LangVersion>13</LangVersion>\n\t\t<OutputType>Library</OutputType>\n\t\t<Nullable>enable</Nullable>\n\t\t<GeneratePackageOnBuild>False</GeneratePackageOnBuild>\n\t\t<Company>Cysharp</Company>\n\t\t<GenerateDocumentationFile>true</GenerateDocumentationFile>\n\t\t<NoWarn>1701;1702;1705;1591</NoWarn>\n\t\t<RootNamespace>MasterMemory</RootNamespace>\n\n\t\t<!-- NuGet -->\n\t\t<PackageId>MasterMemory.Annotations</PackageId>\n\t\t<Description>Attributes of MasterMemory.</Description>\n\t\t<IsPackable>true</IsPackable>\n\t</PropertyGroup>\n\n</Project>\n"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/DiagnosticDescriptors.cs",
    "content": "﻿using Microsoft.CodeAnalysis;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace MasterMemory;\n\ninternal sealed class DiagnosticReporter : IEquatable<DiagnosticReporter>\n{\n    List<Diagnostic>? diagnostics;\n\n    public bool HasDiagnostics => diagnostics != null && diagnostics.Count != 0;\n\n    public void ReportDiagnostic(DiagnosticDescriptor diagnosticDescriptor, Location location, params object?[]? messageArgs)\n    {\n        var diagnostic = Diagnostic.Create(diagnosticDescriptor, location, messageArgs);\n        if (diagnostics == null)\n        {\n            diagnostics = new();\n        }\n        diagnostics.Add(diagnostic);\n    }\n\n    public void ReportToContext(SourceProductionContext context)\n    {\n        if (diagnostics != null)\n        {\n            foreach (var item in diagnostics)\n            {\n                context.ReportDiagnostic(item);\n            }\n        }\n    }\n\n    public bool Equals(DiagnosticReporter other)\n    {\n        // if error, always false and otherwise ignore\n        if (diagnostics == null && other.diagnostics == null)\n        {\n            return true;\n        }\n\n        return false;\n    }\n}\n\ninternal static class DiagnosticDescriptors\n{\n    const string Category = \"GenerateMasterMemory\";\n\n    public static void ReportDiagnostic(this SourceProductionContext context, DiagnosticDescriptor diagnosticDescriptor, Location location, params object?[]? messageArgs)\n    {\n        var diagnostic = Diagnostic.Create(diagnosticDescriptor, location, messageArgs);\n        context.ReportDiagnostic(diagnostic);\n    }\n\n    public static DiagnosticDescriptor Create(int id, string message)\n    {\n        return Create(id, message, message);\n    }\n\n    public static DiagnosticDescriptor Create(int id, string title, string messageFormat)\n    {\n        return new DiagnosticDescriptor(\n            id: \"MAM\" + id.ToString(\"000\"),\n            title: title,\n            messageFormat: messageFormat,\n            category: Category,\n            defaultSeverity: DiagnosticSeverity.Error,\n            isEnabledByDefault: true);\n    }\n\n    public static DiagnosticDescriptor RequirePrimaryKey { get; } = Create(\n        1,\n        \"MemoryTable does not found PrimaryKey property, Type:{0}.\");\n\n    public static DiagnosticDescriptor DuplicatePrimaryKey { get; } = Create(\n        2,\n        \"Duplicate PrimaryKey:{0}.{1}\");\n\n    public static DiagnosticDescriptor DuplicateSecondaryKey { get; } = Create(\n        3,\n        \"Duplicate SecondaryKey, doesn't allow to add multiple attribute in same attribute list:{0}.{1}\");\n}\n"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/GeneratorCore/CodeGenerator.cs",
    "content": "﻿#nullable disable\n\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\n\nnamespace MasterMemory.GeneratorCore\n{\n    internal static class CodeGenerator\n    {\n        // return GenerationContext?\n        public static GenerationContext CreateGenerationContext(TypeDeclarationSyntax classDecl, AttributeData memoryTableAttribute, DiagnosticReporter reporter)\n        {\n            var context = new GenerationContext();\n\n            context.ClassName = classDecl.Identifier.ToFullString().Trim();\n            context.MemoryTableName = memoryTableAttribute.ConstructorArguments[0].Value as string ?? context.ClassName;\n\n            var hasError = false;\n            var members = classDecl.Members.OfType<PropertyDeclarationSyntax>()\n                .Select(x =>\n                {\n                    var prop = ExtractPropertyAttribute(x, reporter);\n                    if (prop == null)\n                    {\n                        hasError = true;\n                        return default!;\n                    }\n                    return prop.Value;\n                })\n                .ToArray();\n            if (hasError) return null;\n\n            var primaryKey = AggregatePrimaryKey(members.Where(x => x.Item1 != null).Select(x => x.Item1));\n            if (primaryKey.Properties.Length == 0)\n            {\n                reporter.ReportDiagnostic(DiagnosticDescriptors.RequirePrimaryKey, classDecl.Identifier.GetLocation(), context.ClassName);\n                return null;\n            }\n\n            var secondaryKeys = members.SelectMany(x => x.Item2).GroupBy(x => x.IndexNo).Select(x => AggregateSecondaryKey(x)).ToArray();\n            var properties = members.Where(x => x.Item3 != null).Select(x => new Property\n            {\n                Type = x.Item3.Type.ToFullStringTrim(),\n                Name = x.Item3.Identifier.Text,\n            }).ToArray();\n\n            var root = classDecl.SyntaxTree.GetRoot();\n\n            var ns = root.DescendantNodes().OfType<NamespaceDeclarationSyntax>()\n                .Select(x => \"using \" + x.Name.ToFullStringTrim() + \";\")\n                .ToArray();\n\n            var usingStrings = root.DescendantNodes()\n                .OfType<UsingDirectiveSyntax>()\n                .Select(x => x.ToFullString().Trim())\n                .Concat(new[] { \"using MasterMemory\", \"using MasterMemory.Validation\", \"using System\", \"using System.Collections.Generic\" })\n                .Concat(ns)\n                .Select(x => x.Trim(';') + \";\")\n                .Distinct()\n                .OrderBy(x => x, StringComparer.Ordinal)\n                .ToArray();\n\n            context.PrimaryKey = primaryKey;\n            context.SecondaryKeys = secondaryKeys;\n            context.Properties = properties;\n            context.UsingStrings = usingStrings;\n            context.OriginalClassDeclaration = classDecl;\n            return context;\n        }\n\n\n        static (PrimaryKey, List<SecondaryKey>, PropertyDeclarationSyntax)? ExtractPropertyAttribute(PropertyDeclarationSyntax property, DiagnosticReporter reporter)\n        {\n            // Attribute Parterns:\n            // Primarykey(keyOrder = 0)\n            // SecondaryKey(indexNo, keyOrder = 0)\n            // NonUnique\n            // StringComparisonOption\n\n            PrimaryKey resultPrimaryKey = default;\n            List<SecondaryKey> secondaryKeys = new List<SecondaryKey>();\n            bool isSerializableProperty = true;\n\n            foreach (var attrList in property.AttributeLists)\n            {\n                var hasNonUnique = false;\n                PrimaryKey primaryKey = default;\n                SecondaryKey secondaryKey = default;\n\n                foreach (var attr in attrList.Attributes)\n                {\n                    var attrName = attr.Name.ToFullString().Trim();\n                    if (attrName == \"PrimaryKey\" || attrName == \"MasterMemory.PrimaryKey\")\n                    {\n                        if (resultPrimaryKey != null)\n                        {\n                            // PrimaryKey is AllowMultiple:false so this code is dead\n                            reporter.ReportDiagnostic(DiagnosticDescriptors.DuplicatePrimaryKey, property.Identifier.GetLocation(), property.Type.ToFullString(), property.Identifier.ToFullString());\n                            return null;\n                        }\n\n                        primaryKey = new PrimaryKey();\n                        var keyProperty = new KeyProperty()\n                        {\n                            Name = property.Identifier.ToFullStringTrim(),\n                            TypeName = property.Type.ToFullStringTrim()\n                        };\n\n                        foreach (var arg in attr.ArgumentList?.Arguments ?? default)\n                        {\n                            keyProperty.KeyOrder = (int)((arg.Expression as LiteralExpressionSyntax).Token.Value);\n                        }\n\n                        primaryKey.Properties = new[] { keyProperty };\n                    }\n                    else if (attrName == \"SecondaryKey\" || attrName == \"MasterMemory.SecondaryKey\")\n                    {\n                        if (secondaryKey != null)\n                        {\n                            reporter.ReportDiagnostic(DiagnosticDescriptors.DuplicateSecondaryKey, property.Identifier.GetLocation(), property.Type.ToFullString(), property.Identifier.ToFullString());\n                            return null;\n                        }\n\n                        secondaryKey = new SecondaryKey();\n                        var keyProperty = new KeyProperty()\n                        {\n                            Name = property.Identifier.ToFullStringTrim(),\n                            TypeName = property.Type.ToFullStringTrim()\n                        };\n\n                        var args = attr.ArgumentList.Arguments;\n                        secondaryKey.IndexNo = (int)((args[0].Expression as LiteralExpressionSyntax).Token.Value);\n                        if (args.Count == 2)\n                        {\n                            keyProperty.KeyOrder = (int)((args[1].Expression as LiteralExpressionSyntax).Token.Value);\n                        }\n                        secondaryKey.Properties = new[] { keyProperty };\n                    }\n                    else if (attrName == \"NonUnique\" || attrName == \"MasterMemory.NonUnique\")\n                    {\n                        hasNonUnique = true;\n                    }\n                    else if (attrName == \"StringComparisonOption\" || attrName == \"MasterMemory.StringComparisonOption\")\n                    {\n                        var option = (attr.ArgumentList.Arguments[0].Expression as MemberAccessExpressionSyntax).ToFullStringTrim();\n                        if (primaryKey != null)\n                        {\n                            primaryKey.StringComparisonOption = option;\n                        }\n                        if (secondaryKey != null)\n                        {\n                            secondaryKey.StringComparisonOption = option;\n                        }\n                    }\n                    else if (!property.Modifiers.Any(SyntaxKind.PublicKeyword)\n                        || attrName == \"IgnoreMember\" || attrName == \"MessagePack.IgnoreMember\"\n                        || attrName == \"IgnoreDataMember\" || attrName == \"System.Runtime.Serialization.IgnoreDataMember\")\n                    {\n                        isSerializableProperty = false;\n                    }\n                }\n\n                if (hasNonUnique)\n                {\n                    if (primaryKey != null)\n                    {\n                        primaryKey.IsNonUnique = true;\n                    }\n                    if (secondaryKey != null)\n                    {\n                        secondaryKey.IsNonUnique = true;\n                    }\n                }\n\n                if (primaryKey != null)\n                {\n                    resultPrimaryKey = primaryKey;\n                }\n                if (secondaryKey != null)\n                {\n                    secondaryKeys.Add(secondaryKey);\n                }\n            }\n\n            return (resultPrimaryKey, secondaryKeys, isSerializableProperty ? property : null);\n        }\n\n        static PrimaryKey AggregatePrimaryKey(IEnumerable<PrimaryKey> primaryKeys)\n        {\n            var primarykey = new PrimaryKey();\n            var list = new List<KeyProperty>();\n\n            foreach (var item in primaryKeys)\n            {\n                if (item.IsNonUnique) primarykey.IsNonUnique = true;\n                if (item.StringComparisonOption != null) primarykey.StringComparisonOption = item.StringComparisonOption;\n\n                list.AddRange(item.Properties);\n            }\n\n            primarykey.Properties = list.OrderBy(x => x.KeyOrder).ToArray();\n\n            return primarykey;\n        }\n\n        // grouped by IndexNo.\n        static SecondaryKey AggregateSecondaryKey(IGrouping<int, SecondaryKey> secondaryKeys)\n        {\n            var secondaryKey = new SecondaryKey();\n            secondaryKey.IndexNo = secondaryKeys.Key;\n\n            var list = new List<KeyProperty>();\n\n            foreach (var item in secondaryKeys)\n            {\n                if (item.IsNonUnique) secondaryKey.IsNonUnique = true;\n                if (item.StringComparisonOption != null) secondaryKey.StringComparisonOption = item.StringComparisonOption;\n\n                list.AddRange(item.Properties);\n            }\n\n            secondaryKey.Properties = list.OrderBy(x => x.KeyOrder).ToArray();\n            return secondaryKey;\n        }\n    }\n\n    internal static class Extensions\n    {\n        public static string ToFullStringTrim(this SyntaxNode node)\n        {\n            return node.ToFullString().Trim();\n        }\n\n        public static string ToFullStringTrim(this SyntaxToken token)\n        {\n            return token.ToFullString().Trim();\n        }\n    }\n}\n"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/GeneratorCore/DatabaseBuilderTemplate.cs",
    "content": "﻿// ------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version: 17.0.0.0\n//  \n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n// ------------------------------------------------------------------------------\nnamespace MasterMemory.GeneratorCore\n{\n    using System.Linq;\n    using System.Text;\n    using System.Collections.Generic;\n    using System;\n    \n    /// <summary>\n    /// Class to produce the template output\n    /// </summary>\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.TextTemplating\", \"17.0.0.0\")]\n    public partial class DatabaseBuilderTemplate : DatabaseBuilderTemplateBase\n    {\n        /// <summary>\n        /// Create the template output\n        /// </summary>\n        public virtual string TransformText()\n        {\n            this.Write(\"// <auto-generated />\\r\\n#pragma warning disable\\r\\n#nullable enable\\r\\n\\r\\n\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(Using));\n            this.Write(\"\\r\\n\\r\\nnamespace \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(Namespace));\n            this.Write(\"\\r\\n{\\r\\n   public sealed class \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(ClassName));\n            this.Write(\" : DatabaseBuilderBase\\r\\n   {\\r\\n        public \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(ClassName));\n            this.Write(\"() : this(null) { }\\r\\n        public \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(ClassName));\n            this.Write(\"(MessagePack.IFormatterResolver? resolver) : base(resolver) { }\\r\\n\\r\\n\");\n foreach(var item in GenerationContexts) { \n            this.Write(\"        public \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(ClassName));\n            this.Write(\" Append(System.Collections.Generic.IEnumerable<\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"> dataSource)\\r\\n        {\\r\\n            AppendCore(dataSource, x => \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.PrimaryKey.BuildKeyAccessor(\"x\")));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.PrimaryKey.BuildComparer()));\n            this.Write(\");\\r\\n            return this;\\r\\n        }\\r\\n\\r\\n\");\n } \n            this.Write(\"    }\\r\\n}\");\n            return this.GenerationEnvironment.ToString();\n        }\n    }\n    #region Base class\n    /// <summary>\n    /// Base class for this transformation\n    /// </summary>\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.TextTemplating\", \"17.0.0.0\")]\n    public class DatabaseBuilderTemplateBase\n    {\n        #region Fields\n        private global::System.Text.StringBuilder generationEnvironmentField;\n        private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField;\n        private global::System.Collections.Generic.List<int> indentLengthsField;\n        private string currentIndentField = \"\";\n        private bool endsWithNewline;\n        private global::System.Collections.Generic.IDictionary<string, object> sessionField;\n        #endregion\n        #region Properties\n        /// <summary>\n        /// The string builder that generation-time code is using to assemble generated output\n        /// </summary>\n        public System.Text.StringBuilder GenerationEnvironment\n        {\n            get\n            {\n                if ((this.generationEnvironmentField == null))\n                {\n                    this.generationEnvironmentField = new global::System.Text.StringBuilder();\n                }\n                return this.generationEnvironmentField;\n            }\n            set\n            {\n                this.generationEnvironmentField = value;\n            }\n        }\n        /// <summary>\n        /// The error collection for the generation process\n        /// </summary>\n        public System.CodeDom.Compiler.CompilerErrorCollection Errors\n        {\n            get\n            {\n                if ((this.errorsField == null))\n                {\n                    this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection();\n                }\n                return this.errorsField;\n            }\n        }\n        /// <summary>\n        /// A list of the lengths of each indent that was added with PushIndent\n        /// </summary>\n        private System.Collections.Generic.List<int> indentLengths\n        {\n            get\n            {\n                if ((this.indentLengthsField == null))\n                {\n                    this.indentLengthsField = new global::System.Collections.Generic.List<int>();\n                }\n                return this.indentLengthsField;\n            }\n        }\n        /// <summary>\n        /// Gets the current indent we use when adding lines to the output\n        /// </summary>\n        public string CurrentIndent\n        {\n            get\n            {\n                return this.currentIndentField;\n            }\n        }\n        /// <summary>\n        /// Current transformation session\n        /// </summary>\n        public virtual global::System.Collections.Generic.IDictionary<string, object> Session\n        {\n            get\n            {\n                return this.sessionField;\n            }\n            set\n            {\n                this.sessionField = value;\n            }\n        }\n        #endregion\n        #region Transform-time helpers\n        /// <summary>\n        /// Write text directly into the generated output\n        /// </summary>\n        public void Write(string textToAppend)\n        {\n            if (string.IsNullOrEmpty(textToAppend))\n            {\n                return;\n            }\n            // If we're starting off, or if the previous text ended with a newline,\n            // we have to append the current indent first.\n            if (((this.GenerationEnvironment.Length == 0) \n                        || this.endsWithNewline))\n            {\n                this.GenerationEnvironment.Append(this.currentIndentField);\n                this.endsWithNewline = false;\n            }\n            // Check if the current text ends with a newline\n            if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture))\n            {\n                this.endsWithNewline = true;\n            }\n            // This is an optimization. If the current indent is \"\", then we don't have to do any\n            // of the more complex stuff further down.\n            if ((this.currentIndentField.Length == 0))\n            {\n                this.GenerationEnvironment.Append(textToAppend);\n                return;\n            }\n            // Everywhere there is a newline in the text, add an indent after it\n            textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField));\n            // If the text ends with a newline, then we should strip off the indent added at the very end\n            // because the appropriate indent will be added when the next time Write() is called\n            if (this.endsWithNewline)\n            {\n                this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length));\n            }\n            else\n            {\n                this.GenerationEnvironment.Append(textToAppend);\n            }\n        }\n        /// <summary>\n        /// Write text directly into the generated output\n        /// </summary>\n        public void WriteLine(string textToAppend)\n        {\n            this.Write(textToAppend);\n            this.GenerationEnvironment.AppendLine();\n            this.endsWithNewline = true;\n        }\n        /// <summary>\n        /// Write formatted text directly into the generated output\n        /// </summary>\n        public void Write(string format, params object[] args)\n        {\n            this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));\n        }\n        /// <summary>\n        /// Write formatted text directly into the generated output\n        /// </summary>\n        public void WriteLine(string format, params object[] args)\n        {\n            this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));\n        }\n        /// <summary>\n        /// Raise an error\n        /// </summary>\n        public void Error(string message)\n        {\n            System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();\n            error.ErrorText = message;\n            this.Errors.Add(error);\n        }\n        /// <summary>\n        /// Raise a warning\n        /// </summary>\n        public void Warning(string message)\n        {\n            System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();\n            error.ErrorText = message;\n            error.IsWarning = true;\n            this.Errors.Add(error);\n        }\n        /// <summary>\n        /// Increase the indent\n        /// </summary>\n        public void PushIndent(string indent)\n        {\n            if ((indent == null))\n            {\n                throw new global::System.ArgumentNullException(\"indent\");\n            }\n            this.currentIndentField = (this.currentIndentField + indent);\n            this.indentLengths.Add(indent.Length);\n        }\n        /// <summary>\n        /// Remove the last indent that was added with PushIndent\n        /// </summary>\n        public string PopIndent()\n        {\n            string returnValue = \"\";\n            if ((this.indentLengths.Count > 0))\n            {\n                int indentLength = this.indentLengths[(this.indentLengths.Count - 1)];\n                this.indentLengths.RemoveAt((this.indentLengths.Count - 1));\n                if ((indentLength > 0))\n                {\n                    returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength));\n                    this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength));\n                }\n            }\n            return returnValue;\n        }\n        /// <summary>\n        /// Remove any indentation\n        /// </summary>\n        public void ClearIndent()\n        {\n            this.indentLengths.Clear();\n            this.currentIndentField = \"\";\n        }\n        #endregion\n        #region ToString Helpers\n        /// <summary>\n        /// Utility class to produce culture-oriented representation of an object as a string.\n        /// </summary>\n        public class ToStringInstanceHelper\n        {\n            private System.IFormatProvider formatProviderField  = global::System.Globalization.CultureInfo.InvariantCulture;\n            /// <summary>\n            /// Gets or sets format provider to be used by ToStringWithCulture method.\n            /// </summary>\n            public System.IFormatProvider FormatProvider\n            {\n                get\n                {\n                    return this.formatProviderField ;\n                }\n                set\n                {\n                    if ((value != null))\n                    {\n                        this.formatProviderField  = value;\n                    }\n                }\n            }\n            /// <summary>\n            /// This is called from the compile/run appdomain to convert objects within an expression block to a string\n            /// </summary>\n            public string ToStringWithCulture(object objectToConvert)\n            {\n                if ((objectToConvert == null))\n                {\n                    throw new global::System.ArgumentNullException(\"objectToConvert\");\n                }\n                System.Type t = objectToConvert.GetType();\n                System.Reflection.MethodInfo method = t.GetMethod(\"ToString\", new System.Type[] {\n                            typeof(System.IFormatProvider)});\n                if ((method == null))\n                {\n                    return objectToConvert.ToString();\n                }\n                else\n                {\n                    return ((string)(method.Invoke(objectToConvert, new object[] {\n                                this.formatProviderField })));\n                }\n            }\n        }\n        private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper();\n        /// <summary>\n        /// Helper to produce culture-oriented representation of an object as a string\n        /// </summary>\n        public ToStringInstanceHelper ToStringHelper\n        {\n            get\n            {\n                return this.toStringHelperField;\n            }\n        }\n        #endregion\n    }\n    #endregion\n}\n"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/GeneratorCore/DatabaseBuilderTemplate.tt",
    "content": "﻿<#@ template debug=\"false\" hostspecific=\"false\" linePragmas=\"false\" language=\"C#\" #>\n<#@ assembly name=\"System.Core\" #>\n<#@ import namespace=\"System.Linq\" #>\n<#@ import namespace=\"System.Text\" #>\n<#@ import namespace=\"System.Collections.Generic\" #>\n// <auto-generated />\n#pragma warning disable\n#nullable enable\n\n<#= Using #>\n\nnamespace <#= Namespace #>\n{\n   public sealed class <#= ClassName #> : DatabaseBuilderBase\n   {\n        public <#= ClassName #>() : this(null) { }\n        public <#= ClassName #>(MessagePack.IFormatterResolver? resolver) : base(resolver) { }\n\n<# foreach(var item in GenerationContexts) { #>\n        public <#= ClassName #> Append(System.Collections.Generic.IEnumerable<<#= item.ClassName #>> dataSource)\n        {\n            AppendCore(dataSource, x => <#= item.PrimaryKey.BuildKeyAccessor(\"x\") #>, <#= item.PrimaryKey.BuildComparer() #>);\n            return this;\n        }\n\n<# } #>\n    }\n}"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/GeneratorCore/GenerationContext.cs",
    "content": "﻿#nullable disable\n\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing System.Linq;\n\nnamespace MasterMemory.GeneratorCore\n{\n\n    public record GenerationContext\n    {\n        public string ClassName { get; set; }\n        public string MemoryTableName { get; set; }\n        public EquatableArray<string> UsingStrings { get; set; }\n        public PrimaryKey PrimaryKey { get; set; }\n        public EquatableArray<SecondaryKey> SecondaryKeys { get; set; }\n\n        // public string InputFilePath { get; set; }\n        public IgnoreEquality<TypeDeclarationSyntax> OriginalClassDeclaration { get; set; }\n\n        public EquatableArray<Property> Properties { get; set; }\n        public EquatableArray<KeyBase> Keys => new KeyBase[] { PrimaryKey }.Concat(SecondaryKeys).ToArray();\n    }\n\n    public record Property\n    {\n        public string Type { get; set; }\n        public string Name { get; set; }\n    }\n\n    public abstract record KeyBase\n    {\n        public bool IsNonUnique { get; set; }\n        public string StringComparisonOption { get; set; }\n        public EquatableArray<KeyProperty> Properties { get; set; }\n        public abstract string SelectorName { get; }\n        public abstract string TableName { get; }\n        public abstract bool IsPrimary { get; }\n\n        public string BuildKeyAccessor(string lambdaArgument)\n        {\n            if (Properties.Length == 1)\n            {\n                return lambdaArgument + \".\" + Properties[0].Name;\n            }\n            else\n            {\n                return \"(\" + string.Join(\", \", Properties.Select(x => lambdaArgument + \".\" + x.Name)) + \")\";\n            }\n        }\n\n        public string BuildTypeName()\n        {\n            if (Properties.Length == 1)\n            {\n                return Properties[0].TypeName;\n            }\n            else\n            {\n                return \"(\" + string.Join(\", \", Properties.Select(x => x.TypeName + \" \" + x.Name)) + \")\";\n            }\n        }\n\n        public string BuildMethodName()\n        {\n            if (Properties.Length == 1)\n            {\n                return Properties[0].Name;\n            }\n            else\n            {\n                return string.Join(\"And\", Properties.Select(x => x.Name));\n            }\n        }\n\n        public string BuildPropertyTupleName()\n        {\n            if (Properties.Length == 1)\n            {\n                return Properties[0].Name;\n            }\n            else\n            {\n                return \"(\" + string.Join(\", \", Properties.Select(x => x.Name)) + \")\";\n            }\n        }\n\n        public string BuildFindPrefix()\n        {\n            return IsNonUnique ? \"FindMany\" : \"FindUnique\";\n        }\n\n        public string BuildReturnTypeName(string elementName)\n        {\n            return IsNonUnique ? \"RangeView<\" + elementName + \">\" : elementName;\n        }\n\n        public string BuildReturnTypeNameForClosest(string elementName)\n        {\n            return IsNonUnique ? \"RangeView<\" + elementName + \">\" : elementName + \"?\";\n        }\n\n        public string BuildComparer()\n        {\n            if (!IsStringType)\n            {\n                return $\"System.Collections.Generic.Comparer<{BuildTypeName()}>.Default\";\n            }\n            else\n            {\n                if (StringComparisonOption != null)\n                {\n                    return \"System.StringComparer.\" + StringComparisonOption.Split('.').Last();\n                }\n                else\n                {\n                    return \"System.StringComparer.Ordinal\";\n                }\n            }\n        }\n\n        public bool IsIntType\n        {\n            get\n            {\n                if (Properties.Length == 1)\n                {\n                    var typeName = Properties[0].TypeName;\n                    if (typeName == \"int\" || typeName == \"Int32\" || typeName == \"System.Int32\")\n                    {\n                        return true;\n                    }\n                    else\n                    {\n                        return false;\n                    }\n                }\n                else\n                {\n                    return false;\n                }\n            }\n        }\n\n        public bool IsStringType\n        {\n            get\n            {\n                if (Properties.Length == 1)\n                {\n                    var typeName = Properties[0].TypeName;\n                    if (typeName == \"string\" || typeName == \"String\" || typeName == \"System.String\")\n                    {\n                        return true;\n                    }\n                    else\n                    {\n                        return false;\n                    }\n                }\n                else\n                {\n                    return false;\n                }\n            }\n        }\n\n        public bool IsComparableNumberType\n        {\n            get\n            {\n                if (Properties.Length == 1)\n                {\n                    var typeName = Properties[0].TypeName;\n                    if (typeName == \"int\" || typeName == \"Int32\" || typeName == \"System.Int32\"\n                     || typeName == \"long\" || typeName == \"Int64\" || typeName == \"System.Int64\"\n                     || typeName == \"uint\" || typeName == \"UInt32\" || typeName == \"System.UInt32\"\n                     || typeName == \"ulong\" || typeName == \"UInt64\" || typeName == \"System.UInt64\"\n                     || typeName == \"byte\" || typeName == \"Byte\" || typeName == \"System.Byte\"\n                     || typeName == \"sbyte\" || typeName == \"SByte\" || typeName == \"System.SByte\"\n                     )\n                    {\n                        return true;\n                    }\n                    else\n                    {\n                        return false;\n                    }\n                }\n                else\n                {\n                    return false;\n                }\n            }\n        }\n\n        public bool CanInlineBinarySearch\n        {\n            get\n            {\n                return (this is PrimaryKey) && (IsComparableNumberType) && !IsNonUnique;\n            }\n        }\n    }\n\n    public record PrimaryKey : KeyBase\n    {\n        public override string SelectorName => \"primaryIndexSelector\";\n        public override string TableName => \"data\";\n        public override bool IsPrimary => true;\n    }\n\n    public record SecondaryKey : KeyBase\n    {\n        public int IndexNo { get; set; }\n        public override string SelectorName => $\"secondaryIndex{IndexNo}Selector\";\n        public override string TableName => $\"secondaryIndex{IndexNo}\";\n        public override bool IsPrimary => false;\n    }\n\n    public record KeyProperty\n    {\n        public int KeyOrder { get; set; }\n        public string Name { get; set; }\n        public string TypeName { get; set; }\n\n        public override string ToString()\n        {\n            return $\"{TypeName} {Name} : {KeyOrder}\";\n        }\n    }\n}\n"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/GeneratorCore/ImmutableBuilderTemplate.cs",
    "content": "﻿// ------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version: 17.0.0.0\n//  \n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n// ------------------------------------------------------------------------------\nnamespace MasterMemory.GeneratorCore\n{\n    using System.Linq;\n    using System.Text;\n    using System.Collections.Generic;\n    using System;\n    \n    /// <summary>\n    /// Class to produce the template output\n    /// </summary>\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.TextTemplating\", \"17.0.0.0\")]\n    public partial class ImmutableBuilderTemplate : ImmutableBuilderTemplateBase\n    {\n        /// <summary>\n        /// Create the template output\n        /// </summary>\n        public virtual string TransformText()\n        {\n            this.Write(\"// <auto-generated />\\r\\n#pragma warning disable\\r\\n#nullable enable\\r\\n\\r\\n\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(Using));\n            this.Write(\"\\r\\n\\r\\nnamespace \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(Namespace));\n            this.Write(\"\\r\\n{\\r\\n   public sealed class \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(ClassName));\n            this.Write(\" : ImmutableBuilderBase\\r\\n   {\\r\\n        \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(PrefixClassName));\n            this.Write(\"MemoryDatabase memory;\\r\\n\\r\\n        public \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(ClassName));\n            this.Write(\"(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(PrefixClassName));\n            this.Write(\"MemoryDatabase memory)\\r\\n        {\\r\\n            this.memory = memory;\\r\\n        }\\r\\n\" +\n                    \"\\r\\n        public \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(PrefixClassName));\n            this.Write(\"MemoryDatabase Build()\\r\\n        {\\r\\n            return memory;\\r\\n        }\\r\\n\\r\\n\");\n for(var i = 0; i < GenerationContexts.Length; i++) { var item = GenerationContexts[i]; \n            this.Write(\"        public void ReplaceAll(System.Collections.Generic.IList<\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"> data)\\r\\n        {\\r\\n            var newData = CloneAndSortBy(data, x => \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.PrimaryKey.BuildKeyAccessor(\"x\")));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.PrimaryKey.BuildComparer()));\n            this.Write(\");\\r\\n            var table = new \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table(newData);\\r\\n            memory = new \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(PrefixClassName));\n            this.Write(\"MemoryDatabase(\\r\\n\");\n for(var j = 0; j < GenerationContexts.Length; j++) { var item2 = GenerationContexts[j]; \n            this.Write(\"                \");\n            this.Write(this.ToStringHelper.ToStringWithCulture((i == j) ? \"table\" : \"memory.\" + item2.ClassName + \"Table\"));\n            this.Write(this.ToStringHelper.ToStringWithCulture((j == GenerationContexts.Length - 1) ? \"\" : \",\"));\n            this.Write(\"\\r\\n\");\n } \n            this.Write(\"            \\r\\n            );\\r\\n        }\\r\\n\\r\\n\");\n if(!item.PrimaryKey.IsNonUnique) { \n            this.Write(\"        public void Remove\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.PrimaryKey.BuildTypeName()));\n            this.Write(\"[] keys)\\r\\n        {\\r\\n            var data = RemoveCore(memory.\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table.GetRawDataUnsafe(), keys, x => \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.PrimaryKey.BuildKeyAccessor(\"x\")));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.PrimaryKey.BuildComparer()));\n            this.Write(\");\\r\\n            var newData = CloneAndSortBy(data, x => \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.PrimaryKey.BuildKeyAccessor(\"x\")));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.PrimaryKey.BuildComparer()));\n            this.Write(\");\\r\\n            var table = new \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table(newData);\\r\\n            memory = new \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(PrefixClassName));\n            this.Write(\"MemoryDatabase(\\r\\n\");\n for(var j = 0; j < GenerationContexts.Length; j++) { var item2 = GenerationContexts[j]; \n            this.Write(\"                \");\n            this.Write(this.ToStringHelper.ToStringWithCulture((i == j) ? \"table\" : \"memory.\" + item2.ClassName + \"Table\"));\n            this.Write(this.ToStringHelper.ToStringWithCulture((j == GenerationContexts.Length - 1) ? \"\" : \",\"));\n            this.Write(\"\\r\\n\");\n } \n            this.Write(\"            \\r\\n            );\\r\\n        }\\r\\n\\r\\n        public void Diff(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"[] addOrReplaceData)\\r\\n        {\\r\\n            var data = DiffCore(memory.\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table.GetRawDataUnsafe(), addOrReplaceData, x => \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.PrimaryKey.BuildKeyAccessor(\"x\")));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.PrimaryKey.BuildComparer()));\n            this.Write(\");\\r\\n            var newData = CloneAndSortBy(data, x => \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.PrimaryKey.BuildKeyAccessor(\"x\")));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.PrimaryKey.BuildComparer()));\n            this.Write(\");\\r\\n            var table = new \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table(newData);\\r\\n            memory = new \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(PrefixClassName));\n            this.Write(\"MemoryDatabase(\\r\\n\");\n for(var j = 0; j < GenerationContexts.Length; j++) { var item2 = GenerationContexts[j]; \n            this.Write(\"                \");\n            this.Write(this.ToStringHelper.ToStringWithCulture((i == j) ? \"table\" : \"memory.\" + item2.ClassName + \"Table\"));\n            this.Write(this.ToStringHelper.ToStringWithCulture((j == GenerationContexts.Length - 1) ? \"\" : \",\"));\n            this.Write(\"\\r\\n\");\n } \n            this.Write(\"            \\r\\n            );\\r\\n        }\\r\\n\");\n } \n            this.Write(\"\\r\\n\");\n } \n            this.Write(\"    }\\r\\n}\");\n            return this.GenerationEnvironment.ToString();\n        }\n    }\n    #region Base class\n    /// <summary>\n    /// Base class for this transformation\n    /// </summary>\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.TextTemplating\", \"17.0.0.0\")]\n    public class ImmutableBuilderTemplateBase\n    {\n        #region Fields\n        private global::System.Text.StringBuilder generationEnvironmentField;\n        private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField;\n        private global::System.Collections.Generic.List<int> indentLengthsField;\n        private string currentIndentField = \"\";\n        private bool endsWithNewline;\n        private global::System.Collections.Generic.IDictionary<string, object> sessionField;\n        #endregion\n        #region Properties\n        /// <summary>\n        /// The string builder that generation-time code is using to assemble generated output\n        /// </summary>\n        public System.Text.StringBuilder GenerationEnvironment\n        {\n            get\n            {\n                if ((this.generationEnvironmentField == null))\n                {\n                    this.generationEnvironmentField = new global::System.Text.StringBuilder();\n                }\n                return this.generationEnvironmentField;\n            }\n            set\n            {\n                this.generationEnvironmentField = value;\n            }\n        }\n        /// <summary>\n        /// The error collection for the generation process\n        /// </summary>\n        public System.CodeDom.Compiler.CompilerErrorCollection Errors\n        {\n            get\n            {\n                if ((this.errorsField == null))\n                {\n                    this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection();\n                }\n                return this.errorsField;\n            }\n        }\n        /// <summary>\n        /// A list of the lengths of each indent that was added with PushIndent\n        /// </summary>\n        private System.Collections.Generic.List<int> indentLengths\n        {\n            get\n            {\n                if ((this.indentLengthsField == null))\n                {\n                    this.indentLengthsField = new global::System.Collections.Generic.List<int>();\n                }\n                return this.indentLengthsField;\n            }\n        }\n        /// <summary>\n        /// Gets the current indent we use when adding lines to the output\n        /// </summary>\n        public string CurrentIndent\n        {\n            get\n            {\n                return this.currentIndentField;\n            }\n        }\n        /// <summary>\n        /// Current transformation session\n        /// </summary>\n        public virtual global::System.Collections.Generic.IDictionary<string, object> Session\n        {\n            get\n            {\n                return this.sessionField;\n            }\n            set\n            {\n                this.sessionField = value;\n            }\n        }\n        #endregion\n        #region Transform-time helpers\n        /// <summary>\n        /// Write text directly into the generated output\n        /// </summary>\n        public void Write(string textToAppend)\n        {\n            if (string.IsNullOrEmpty(textToAppend))\n            {\n                return;\n            }\n            // If we're starting off, or if the previous text ended with a newline,\n            // we have to append the current indent first.\n            if (((this.GenerationEnvironment.Length == 0) \n                        || this.endsWithNewline))\n            {\n                this.GenerationEnvironment.Append(this.currentIndentField);\n                this.endsWithNewline = false;\n            }\n            // Check if the current text ends with a newline\n            if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture))\n            {\n                this.endsWithNewline = true;\n            }\n            // This is an optimization. If the current indent is \"\", then we don't have to do any\n            // of the more complex stuff further down.\n            if ((this.currentIndentField.Length == 0))\n            {\n                this.GenerationEnvironment.Append(textToAppend);\n                return;\n            }\n            // Everywhere there is a newline in the text, add an indent after it\n            textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField));\n            // If the text ends with a newline, then we should strip off the indent added at the very end\n            // because the appropriate indent will be added when the next time Write() is called\n            if (this.endsWithNewline)\n            {\n                this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length));\n            }\n            else\n            {\n                this.GenerationEnvironment.Append(textToAppend);\n            }\n        }\n        /// <summary>\n        /// Write text directly into the generated output\n        /// </summary>\n        public void WriteLine(string textToAppend)\n        {\n            this.Write(textToAppend);\n            this.GenerationEnvironment.AppendLine();\n            this.endsWithNewline = true;\n        }\n        /// <summary>\n        /// Write formatted text directly into the generated output\n        /// </summary>\n        public void Write(string format, params object[] args)\n        {\n            this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));\n        }\n        /// <summary>\n        /// Write formatted text directly into the generated output\n        /// </summary>\n        public void WriteLine(string format, params object[] args)\n        {\n            this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));\n        }\n        /// <summary>\n        /// Raise an error\n        /// </summary>\n        public void Error(string message)\n        {\n            System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();\n            error.ErrorText = message;\n            this.Errors.Add(error);\n        }\n        /// <summary>\n        /// Raise a warning\n        /// </summary>\n        public void Warning(string message)\n        {\n            System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();\n            error.ErrorText = message;\n            error.IsWarning = true;\n            this.Errors.Add(error);\n        }\n        /// <summary>\n        /// Increase the indent\n        /// </summary>\n        public void PushIndent(string indent)\n        {\n            if ((indent == null))\n            {\n                throw new global::System.ArgumentNullException(\"indent\");\n            }\n            this.currentIndentField = (this.currentIndentField + indent);\n            this.indentLengths.Add(indent.Length);\n        }\n        /// <summary>\n        /// Remove the last indent that was added with PushIndent\n        /// </summary>\n        public string PopIndent()\n        {\n            string returnValue = \"\";\n            if ((this.indentLengths.Count > 0))\n            {\n                int indentLength = this.indentLengths[(this.indentLengths.Count - 1)];\n                this.indentLengths.RemoveAt((this.indentLengths.Count - 1));\n                if ((indentLength > 0))\n                {\n                    returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength));\n                    this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength));\n                }\n            }\n            return returnValue;\n        }\n        /// <summary>\n        /// Remove any indentation\n        /// </summary>\n        public void ClearIndent()\n        {\n            this.indentLengths.Clear();\n            this.currentIndentField = \"\";\n        }\n        #endregion\n        #region ToString Helpers\n        /// <summary>\n        /// Utility class to produce culture-oriented representation of an object as a string.\n        /// </summary>\n        public class ToStringInstanceHelper\n        {\n            private System.IFormatProvider formatProviderField  = global::System.Globalization.CultureInfo.InvariantCulture;\n            /// <summary>\n            /// Gets or sets format provider to be used by ToStringWithCulture method.\n            /// </summary>\n            public System.IFormatProvider FormatProvider\n            {\n                get\n                {\n                    return this.formatProviderField ;\n                }\n                set\n                {\n                    if ((value != null))\n                    {\n                        this.formatProviderField  = value;\n                    }\n                }\n            }\n            /// <summary>\n            /// This is called from the compile/run appdomain to convert objects within an expression block to a string\n            /// </summary>\n            public string ToStringWithCulture(object objectToConvert)\n            {\n                if ((objectToConvert == null))\n                {\n                    throw new global::System.ArgumentNullException(\"objectToConvert\");\n                }\n                System.Type t = objectToConvert.GetType();\n                System.Reflection.MethodInfo method = t.GetMethod(\"ToString\", new System.Type[] {\n                            typeof(System.IFormatProvider)});\n                if ((method == null))\n                {\n                    return objectToConvert.ToString();\n                }\n                else\n                {\n                    return ((string)(method.Invoke(objectToConvert, new object[] {\n                                this.formatProviderField })));\n                }\n            }\n        }\n        private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper();\n        /// <summary>\n        /// Helper to produce culture-oriented representation of an object as a string\n        /// </summary>\n        public ToStringInstanceHelper ToStringHelper\n        {\n            get\n            {\n                return this.toStringHelperField;\n            }\n        }\n        #endregion\n    }\n    #endregion\n}\n"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/GeneratorCore/ImmutableBuilderTemplate.tt",
    "content": "﻿<#@ template debug=\"false\" hostspecific=\"false\" linePragmas=\"false\" language=\"C#\" #>\n<#@ assembly name=\"System.Core\" #>\n<#@ import namespace=\"System.Linq\" #>\n<#@ import namespace=\"System.Text\" #>\n<#@ import namespace=\"System.Collections.Generic\" #>\n// <auto-generated />\n#pragma warning disable\n#nullable enable\n\n<#= Using #>\n\nnamespace <#= Namespace #>\n{\n   public sealed class <#= ClassName #> : ImmutableBuilderBase\n   {\n        <#= PrefixClassName #>MemoryDatabase memory;\n\n        public <#= ClassName #>(<#= PrefixClassName #>MemoryDatabase memory)\n        {\n            this.memory = memory;\n        }\n\n        public <#= PrefixClassName #>MemoryDatabase Build()\n        {\n            return memory;\n        }\n\n<# for(var i = 0; i < GenerationContexts.Length; i++) { var item = GenerationContexts[i]; #>\n        public void ReplaceAll(System.Collections.Generic.IList<<#= item.ClassName #>> data)\n        {\n            var newData = CloneAndSortBy(data, x => <#= item.PrimaryKey.BuildKeyAccessor(\"x\") #>, <#= item.PrimaryKey.BuildComparer() #>);\n            var table = new <#= item.ClassName #>Table(newData);\n            memory = new <#= PrefixClassName #>MemoryDatabase(\n<# for(var j = 0; j < GenerationContexts.Length; j++) { var item2 = GenerationContexts[j]; #>\n                <#= (i == j) ? \"table\" : \"memory.\" + item2.ClassName + \"Table\"  #><#= (j == GenerationContexts.Length - 1) ? \"\" : \",\" #>\n<# } #>            \n            );\n        }\n\n<# if(!item.PrimaryKey.IsNonUnique) { #>\n        public void Remove<#= item.ClassName #>(<#= item.PrimaryKey.BuildTypeName() #>[] keys)\n        {\n            var data = RemoveCore(memory.<#= item.ClassName #>Table.GetRawDataUnsafe(), keys, x => <#= item.PrimaryKey.BuildKeyAccessor(\"x\") #>, <#= item.PrimaryKey.BuildComparer() #>);\n            var newData = CloneAndSortBy(data, x => <#= item.PrimaryKey.BuildKeyAccessor(\"x\") #>, <#= item.PrimaryKey.BuildComparer() #>);\n            var table = new <#= item.ClassName #>Table(newData);\n            memory = new <#= PrefixClassName #>MemoryDatabase(\n<# for(var j = 0; j < GenerationContexts.Length; j++) { var item2 = GenerationContexts[j]; #>\n                <#= (i == j) ? \"table\" : \"memory.\" + item2.ClassName + \"Table\"  #><#= (j == GenerationContexts.Length - 1) ? \"\" : \",\" #>\n<# } #>            \n            );\n        }\n\n        public void Diff(<#= item.ClassName #>[] addOrReplaceData)\n        {\n            var data = DiffCore(memory.<#= item.ClassName #>Table.GetRawDataUnsafe(), addOrReplaceData, x => <#= item.PrimaryKey.BuildKeyAccessor(\"x\") #>, <#= item.PrimaryKey.BuildComparer() #>);\n            var newData = CloneAndSortBy(data, x => <#= item.PrimaryKey.BuildKeyAccessor(\"x\") #>, <#= item.PrimaryKey.BuildComparer() #>);\n            var table = new <#= item.ClassName #>Table(newData);\n            memory = new <#= PrefixClassName #>MemoryDatabase(\n<# for(var j = 0; j < GenerationContexts.Length; j++) { var item2 = GenerationContexts[j]; #>\n                <#= (i == j) ? \"table\" : \"memory.\" + item2.ClassName + \"Table\"  #><#= (j == GenerationContexts.Length - 1) ? \"\" : \",\" #>\n<# } #>            \n            );\n        }\n<# } #>\n\n<# } #>\n    }\n}"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/GeneratorCore/MemoryDatabaseTemplate.cs",
    "content": "﻿// ------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version: 17.0.0.0\n//  \n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n// ------------------------------------------------------------------------------\nnamespace MasterMemory.GeneratorCore\n{\n    using System.Linq;\n    using System.Text;\n    using System.Collections.Generic;\n    using System;\n    \n    /// <summary>\n    /// Class to produce the template output\n    /// </summary>\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.TextTemplating\", \"17.0.0.0\")]\n    public partial class MemoryDatabaseTemplate : MemoryDatabaseTemplateBase\n    {\n        /// <summary>\n        /// Create the template output\n        /// </summary>\n        public virtual string TransformText()\n        {\n            this.Write(\"// <auto-generated />\\r\\n#pragma warning disable\\r\\n#nullable enable\\r\\n\\r\\n\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(Using));\n            this.Write(\"\\r\\n\\r\\nnamespace \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(Namespace));\n            this.Write(\"\\r\\n{\\r\\n   public sealed class \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(ClassName));\n            this.Write(\" : MemoryDatabaseBase\\r\\n   {\\r\\n\");\n foreach(var item in GenerationContexts) { \n            this.Write(\"        public \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table { get; private set; } = default!;\\r\\n\");\n } \n            this.Write(\"\\r\\n        public \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(ClassName));\n            this.Write(\"(\\r\\n\");\n for(var i = 0; i < GenerationContexts.Length; i++) { var item = GenerationContexts[i]; \n            this.Write(\"            \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table\");\n            this.Write(this.ToStringHelper.ToStringWithCulture((i == GenerationContexts.Length - 1) ? \"\" : \",\"));\n            this.Write(\"\\r\\n\");\n } \n            this.Write(\"        )\\r\\n        {\\r\\n\");\n for(var i = 0; i < GenerationContexts.Length; i++) { var item = GenerationContexts[i]; \n            this.Write(\"            this.\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table = \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table;\\r\\n\");\n } \n            this.Write(\"        }\\r\\n\\r\\n        public \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(ClassName));\n            this.Write(@\"(byte[] databaseBinary, bool internString = true, MessagePack.IFormatterResolver? formatterResolver = null, int maxDegreeOfParallelism = 1)\n            : base(databaseBinary, internString, formatterResolver, maxDegreeOfParallelism)\n        {\n        }\n\n        protected override void Init(Dictionary<string, (int offset, int count)> header, System.ReadOnlyMemory<byte> databaseBinary, MessagePack.MessagePackSerializerOptions options, int maxDegreeOfParallelism)\n        {\n            if (maxDegreeOfParallelism == 1)\n            {\n                InitSequential(header, databaseBinary, options, maxDegreeOfParallelism);\n            }\n            else\n            {\n                InitParallel(header, databaseBinary, options, maxDegreeOfParallelism);\n            }\n        }\n\n        void InitSequential(Dictionary<string, (int offset, int count)> header, System.ReadOnlyMemory<byte> databaseBinary, MessagePack.MessagePackSerializerOptions options, int maxDegreeOfParallelism)\n        {\n\");\n foreach(var item in GenerationContexts) { \n            this.Write(\"            this.\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table = ExtractTableData<\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table>(header, databaseBinary, options, xs => new \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table(xs));\\r\\n\");\n } \n            this.Write(@\"        }\n\n        void InitParallel(Dictionary<string, (int offset, int count)> header, System.ReadOnlyMemory<byte> databaseBinary, MessagePack.MessagePackSerializerOptions options, int maxDegreeOfParallelism)\n        {\n            var extracts = new Action[]\n            {\n\");\n foreach(var item in GenerationContexts) { \n            this.Write(\"                () => this.\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table = ExtractTableData<\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table>(header, databaseBinary, options, xs => new \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table(xs)),\\r\\n\");\n } \n            this.Write(@\"            };\n            \n            System.Threading.Tasks.Parallel.Invoke(new System.Threading.Tasks.ParallelOptions\n            {\n                MaxDegreeOfParallelism = maxDegreeOfParallelism\n            }, extracts);\n        }\n\n        public \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(PrefixClassName));\n            this.Write(\"ImmutableBuilder ToImmutableBuilder()\\r\\n        {\\r\\n            return new \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(PrefixClassName));\n            this.Write(\"ImmutableBuilder(this);\\r\\n        }\\r\\n\\r\\n        public \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(PrefixClassName));\n            this.Write(\"DatabaseBuilder ToDatabaseBuilder()\\r\\n        {\\r\\n            var builder = new \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(PrefixClassName));\n            this.Write(\"DatabaseBuilder();\\r\\n\");\n foreach(var item in GenerationContexts) { \n            this.Write(\"            builder.Append(this.\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table.GetRawDataUnsafe());\\r\\n\");\n } \n            this.Write(\"            return builder;\\r\\n        }\\r\\n\\r\\n        public \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(PrefixClassName));\n            this.Write(\"DatabaseBuilder ToDatabaseBuilder(MessagePack.IFormatterResolver resolver)\\r\\n     \" +\n                    \"   {\\r\\n            var builder = new \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(PrefixClassName));\n            this.Write(\"DatabaseBuilder(resolver);\\r\\n\");\n foreach(var item in GenerationContexts) { \n            this.Write(\"            builder.Append(this.\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table.GetRawDataUnsafe());\\r\\n\");\n } \n            this.Write(@\"            return builder;\n        }\n\n#if !DISABLE_MASTERMEMORY_VALIDATOR\n\n        public ValidateResult Validate()\n        {\n            var result = new ValidateResult();\n            var database = new ValidationDatabase(new object[]\n            {\n\");\n foreach(var item in GenerationContexts) { \n            this.Write(\"                \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table,\\r\\n\");\n } \n            this.Write(\"            });\\r\\n\\r\\n\");\n foreach(var item in GenerationContexts) { \n            this.Write(\"            ((ITableUniqueValidate)\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table).ValidateUnique(result);\\r\\n            ValidateTable(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table.All, database, \\\"\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.PrimaryKey.BuildPropertyTupleName()));\n            this.Write(\"\\\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table.PrimaryKeySelector, result);\\r\\n\");\n } \n            this.Write(\"\\r\\n            return result;\\r\\n        }\\r\\n\\r\\n#endif\\r\\n\\r\\n        static MasterMemory.\" +\n                    \"Meta.MetaDatabase? metaTable;\\r\\n\\r\\n        public static object? GetTable(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(ClassName));\n            this.Write(\" db, string tableName)\\r\\n        {\\r\\n            switch (tableName)\\r\\n            {\\r\" +\n                    \"\\n\");\n foreach(var item in GenerationContexts) { \n            this.Write(\"                case \\\"\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.MemoryTableName));\n            this.Write(\"\\\":\\r\\n                    return db.\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table;\\r\\n\");\n } \n            this.Write(@\"                \n                default:\n                    return null;\n            }\n        }\n\n#if !DISABLE_MASTERMEMORY_METADATABASE\n\n        public static MasterMemory.Meta.MetaDatabase GetMetaDatabase()\n        {\n            if (metaTable != null) return metaTable;\n\n            var dict = new Dictionary<string, MasterMemory.Meta.MetaTable>();\n\");\n foreach(var item in GenerationContexts) { \n            this.Write(\"            dict.Add(\\\"\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.MemoryTableName));\n            this.Write(\"\\\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(Namespace));\n            this.Write(\".Tables.\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"Table.CreateMetaTable());\\r\\n\");\n } \n            this.Write(\"\\r\\n            metaTable = new MasterMemory.Meta.MetaDatabase(dict);\\r\\n            \" +\n                    \"return metaTable;\\r\\n        }\\r\\n\\r\\n#endif\\r\\n    }\\r\\n}\");\n            return this.GenerationEnvironment.ToString();\n        }\n    }\n    #region Base class\n    /// <summary>\n    /// Base class for this transformation\n    /// </summary>\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.TextTemplating\", \"17.0.0.0\")]\n    public class MemoryDatabaseTemplateBase\n    {\n        #region Fields\n        private global::System.Text.StringBuilder generationEnvironmentField;\n        private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField;\n        private global::System.Collections.Generic.List<int> indentLengthsField;\n        private string currentIndentField = \"\";\n        private bool endsWithNewline;\n        private global::System.Collections.Generic.IDictionary<string, object> sessionField;\n        #endregion\n        #region Properties\n        /// <summary>\n        /// The string builder that generation-time code is using to assemble generated output\n        /// </summary>\n        public System.Text.StringBuilder GenerationEnvironment\n        {\n            get\n            {\n                if ((this.generationEnvironmentField == null))\n                {\n                    this.generationEnvironmentField = new global::System.Text.StringBuilder();\n                }\n                return this.generationEnvironmentField;\n            }\n            set\n            {\n                this.generationEnvironmentField = value;\n            }\n        }\n        /// <summary>\n        /// The error collection for the generation process\n        /// </summary>\n        public System.CodeDom.Compiler.CompilerErrorCollection Errors\n        {\n            get\n            {\n                if ((this.errorsField == null))\n                {\n                    this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection();\n                }\n                return this.errorsField;\n            }\n        }\n        /// <summary>\n        /// A list of the lengths of each indent that was added with PushIndent\n        /// </summary>\n        private System.Collections.Generic.List<int> indentLengths\n        {\n            get\n            {\n                if ((this.indentLengthsField == null))\n                {\n                    this.indentLengthsField = new global::System.Collections.Generic.List<int>();\n                }\n                return this.indentLengthsField;\n            }\n        }\n        /// <summary>\n        /// Gets the current indent we use when adding lines to the output\n        /// </summary>\n        public string CurrentIndent\n        {\n            get\n            {\n                return this.currentIndentField;\n            }\n        }\n        /// <summary>\n        /// Current transformation session\n        /// </summary>\n        public virtual global::System.Collections.Generic.IDictionary<string, object> Session\n        {\n            get\n            {\n                return this.sessionField;\n            }\n            set\n            {\n                this.sessionField = value;\n            }\n        }\n        #endregion\n        #region Transform-time helpers\n        /// <summary>\n        /// Write text directly into the generated output\n        /// </summary>\n        public void Write(string textToAppend)\n        {\n            if (string.IsNullOrEmpty(textToAppend))\n            {\n                return;\n            }\n            // If we're starting off, or if the previous text ended with a newline,\n            // we have to append the current indent first.\n            if (((this.GenerationEnvironment.Length == 0) \n                        || this.endsWithNewline))\n            {\n                this.GenerationEnvironment.Append(this.currentIndentField);\n                this.endsWithNewline = false;\n            }\n            // Check if the current text ends with a newline\n            if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture))\n            {\n                this.endsWithNewline = true;\n            }\n            // This is an optimization. If the current indent is \"\", then we don't have to do any\n            // of the more complex stuff further down.\n            if ((this.currentIndentField.Length == 0))\n            {\n                this.GenerationEnvironment.Append(textToAppend);\n                return;\n            }\n            // Everywhere there is a newline in the text, add an indent after it\n            textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField));\n            // If the text ends with a newline, then we should strip off the indent added at the very end\n            // because the appropriate indent will be added when the next time Write() is called\n            if (this.endsWithNewline)\n            {\n                this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length));\n            }\n            else\n            {\n                this.GenerationEnvironment.Append(textToAppend);\n            }\n        }\n        /// <summary>\n        /// Write text directly into the generated output\n        /// </summary>\n        public void WriteLine(string textToAppend)\n        {\n            this.Write(textToAppend);\n            this.GenerationEnvironment.AppendLine();\n            this.endsWithNewline = true;\n        }\n        /// <summary>\n        /// Write formatted text directly into the generated output\n        /// </summary>\n        public void Write(string format, params object[] args)\n        {\n            this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));\n        }\n        /// <summary>\n        /// Write formatted text directly into the generated output\n        /// </summary>\n        public void WriteLine(string format, params object[] args)\n        {\n            this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));\n        }\n        /// <summary>\n        /// Raise an error\n        /// </summary>\n        public void Error(string message)\n        {\n            System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();\n            error.ErrorText = message;\n            this.Errors.Add(error);\n        }\n        /// <summary>\n        /// Raise a warning\n        /// </summary>\n        public void Warning(string message)\n        {\n            System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();\n            error.ErrorText = message;\n            error.IsWarning = true;\n            this.Errors.Add(error);\n        }\n        /// <summary>\n        /// Increase the indent\n        /// </summary>\n        public void PushIndent(string indent)\n        {\n            if ((indent == null))\n            {\n                throw new global::System.ArgumentNullException(\"indent\");\n            }\n            this.currentIndentField = (this.currentIndentField + indent);\n            this.indentLengths.Add(indent.Length);\n        }\n        /// <summary>\n        /// Remove the last indent that was added with PushIndent\n        /// </summary>\n        public string PopIndent()\n        {\n            string returnValue = \"\";\n            if ((this.indentLengths.Count > 0))\n            {\n                int indentLength = this.indentLengths[(this.indentLengths.Count - 1)];\n                this.indentLengths.RemoveAt((this.indentLengths.Count - 1));\n                if ((indentLength > 0))\n                {\n                    returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength));\n                    this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength));\n                }\n            }\n            return returnValue;\n        }\n        /// <summary>\n        /// Remove any indentation\n        /// </summary>\n        public void ClearIndent()\n        {\n            this.indentLengths.Clear();\n            this.currentIndentField = \"\";\n        }\n        #endregion\n        #region ToString Helpers\n        /// <summary>\n        /// Utility class to produce culture-oriented representation of an object as a string.\n        /// </summary>\n        public class ToStringInstanceHelper\n        {\n            private System.IFormatProvider formatProviderField  = global::System.Globalization.CultureInfo.InvariantCulture;\n            /// <summary>\n            /// Gets or sets format provider to be used by ToStringWithCulture method.\n            /// </summary>\n            public System.IFormatProvider FormatProvider\n            {\n                get\n                {\n                    return this.formatProviderField ;\n                }\n                set\n                {\n                    if ((value != null))\n                    {\n                        this.formatProviderField  = value;\n                    }\n                }\n            }\n            /// <summary>\n            /// This is called from the compile/run appdomain to convert objects within an expression block to a string\n            /// </summary>\n            public string ToStringWithCulture(object objectToConvert)\n            {\n                if ((objectToConvert == null))\n                {\n                    throw new global::System.ArgumentNullException(\"objectToConvert\");\n                }\n                System.Type t = objectToConvert.GetType();\n                System.Reflection.MethodInfo method = t.GetMethod(\"ToString\", new System.Type[] {\n                            typeof(System.IFormatProvider)});\n                if ((method == null))\n                {\n                    return objectToConvert.ToString();\n                }\n                else\n                {\n                    return ((string)(method.Invoke(objectToConvert, new object[] {\n                                this.formatProviderField })));\n                }\n            }\n        }\n        private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper();\n        /// <summary>\n        /// Helper to produce culture-oriented representation of an object as a string\n        /// </summary>\n        public ToStringInstanceHelper ToStringHelper\n        {\n            get\n            {\n                return this.toStringHelperField;\n            }\n        }\n        #endregion\n    }\n    #endregion\n}\n"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/GeneratorCore/MemoryDatabaseTemplate.tt",
    "content": "﻿<#@ template debug=\"false\" hostspecific=\"false\" linePragmas=\"false\" language=\"C#\" #>\n<#@ assembly name=\"System.Core\" #>\n<#@ import namespace=\"System.Linq\" #>\n<#@ import namespace=\"System.Text\" #>\n<#@ import namespace=\"System.Collections.Generic\" #>\n// <auto-generated />\n#pragma warning disable\n#nullable enable\n\n<#= Using #>\n\nnamespace <#= Namespace #>\n{\n   public sealed class <#= ClassName #> : MemoryDatabaseBase\n   {\n<# foreach(var item in GenerationContexts) { #>\n        public <#= item.ClassName #>Table <#= item.ClassName #>Table { get; private set; } = default!;\n<# } #>\n\n        public <#= ClassName #>(\n<# for(var i = 0; i < GenerationContexts.Length; i++) { var item = GenerationContexts[i]; #>\n            <#= item.ClassName #>Table <#= item.ClassName #>Table<#= (i == GenerationContexts.Length - 1) ? \"\" : \",\" #>\n<# } #>\n        )\n        {\n<# for(var i = 0; i < GenerationContexts.Length; i++) { var item = GenerationContexts[i]; #>\n            this.<#= item.ClassName #>Table = <#= item.ClassName #>Table;\n<# } #>\n        }\n\n        public <#= ClassName #>(byte[] databaseBinary, bool internString = true, MessagePack.IFormatterResolver? formatterResolver = null, int maxDegreeOfParallelism = 1)\n            : base(databaseBinary, internString, formatterResolver, maxDegreeOfParallelism)\n        {\n        }\n\n        protected override void Init(Dictionary<string, (int offset, int count)> header, System.ReadOnlyMemory<byte> databaseBinary, MessagePack.MessagePackSerializerOptions options, int maxDegreeOfParallelism)\n        {\n            if (maxDegreeOfParallelism == 1)\n            {\n                InitSequential(header, databaseBinary, options, maxDegreeOfParallelism);\n            }\n            else\n            {\n                InitParallel(header, databaseBinary, options, maxDegreeOfParallelism);\n            }\n        }\n\n        void InitSequential(Dictionary<string, (int offset, int count)> header, System.ReadOnlyMemory<byte> databaseBinary, MessagePack.MessagePackSerializerOptions options, int maxDegreeOfParallelism)\n        {\n<# foreach(var item in GenerationContexts) { #>\n            this.<#= item.ClassName #>Table = ExtractTableData<<#= item.ClassName #>, <#= item.ClassName #>Table>(header, databaseBinary, options, xs => new <#= item.ClassName #>Table(xs));\n<# } #>\n        }\n\n        void InitParallel(Dictionary<string, (int offset, int count)> header, System.ReadOnlyMemory<byte> databaseBinary, MessagePack.MessagePackSerializerOptions options, int maxDegreeOfParallelism)\n        {\n            var extracts = new Action[]\n            {\n<# foreach(var item in GenerationContexts) { #>\n                () => this.<#= item.ClassName #>Table = ExtractTableData<<#= item.ClassName #>, <#= item.ClassName #>Table>(header, databaseBinary, options, xs => new <#= item.ClassName #>Table(xs)),\n<# } #>\n            };\n            \n            System.Threading.Tasks.Parallel.Invoke(new System.Threading.Tasks.ParallelOptions\n            {\n                MaxDegreeOfParallelism = maxDegreeOfParallelism\n            }, extracts);\n        }\n\n        public <#= PrefixClassName #>ImmutableBuilder ToImmutableBuilder()\n        {\n            return new <#= PrefixClassName #>ImmutableBuilder(this);\n        }\n\n        public <#= PrefixClassName #>DatabaseBuilder ToDatabaseBuilder()\n        {\n            var builder = new <#= PrefixClassName #>DatabaseBuilder();\n<# foreach(var item in GenerationContexts) { #>\n            builder.Append(this.<#= item.ClassName #>Table.GetRawDataUnsafe());\n<# } #>\n            return builder;\n        }\n\n        public <#= PrefixClassName #>DatabaseBuilder ToDatabaseBuilder(MessagePack.IFormatterResolver resolver)\n        {\n            var builder = new <#= PrefixClassName #>DatabaseBuilder(resolver);\n<# foreach(var item in GenerationContexts) { #>\n            builder.Append(this.<#= item.ClassName #>Table.GetRawDataUnsafe());\n<# } #>\n            return builder;\n        }\n\n#if !DISABLE_MASTERMEMORY_VALIDATOR\n\n        public ValidateResult Validate()\n        {\n            var result = new ValidateResult();\n            var database = new ValidationDatabase(new object[]\n            {\n<# foreach(var item in GenerationContexts) { #>\n                <#= item.ClassName #>Table,\n<# } #>\n            });\n\n<# foreach(var item in GenerationContexts) { #>\n            ((ITableUniqueValidate)<#= item.ClassName #>Table).ValidateUnique(result);\n            ValidateTable(<#= item.ClassName #>Table.All, database, \"<#= item.PrimaryKey.BuildPropertyTupleName() #>\", <#= item.ClassName #>Table.PrimaryKeySelector, result);\n<# } #>\n\n            return result;\n        }\n\n#endif\n\n        static MasterMemory.Meta.MetaDatabase? metaTable;\n\n        public static object? GetTable(<#= ClassName #> db, string tableName)\n        {\n            switch (tableName)\n            {\n<# foreach(var item in GenerationContexts) { #>\n                case \"<#= item.MemoryTableName #>\":\n                    return db.<#= item.ClassName #>Table;\n<# } #>                \n                default:\n                    return null;\n            }\n        }\n\n#if !DISABLE_MASTERMEMORY_METADATABASE\n\n        public static MasterMemory.Meta.MetaDatabase GetMetaDatabase()\n        {\n            if (metaTable != null) return metaTable;\n\n            var dict = new Dictionary<string, MasterMemory.Meta.MetaTable>();\n<# foreach(var item in GenerationContexts) { #>\n            dict.Add(\"<#= item.MemoryTableName #>\", <#= Namespace #>.Tables.<#= item.ClassName #>Table.CreateMetaTable());\n<# } #>\n\n            metaTable = new MasterMemory.Meta.MetaDatabase(dict);\n            return metaTable;\n        }\n\n#endif\n    }\n}"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/GeneratorCore/MessagePackResolverTemplate.cs",
    "content": "﻿// ------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version: 17.0.0.0\n//  \n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n// ------------------------------------------------------------------------------\nnamespace MasterMemory.GeneratorCore\n{\n    using System.Linq;\n    using System.Text;\n    using System.Collections.Generic;\n    using System;\n    \n    /// <summary>\n    /// Class to produce the template output\n    /// </summary>\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.TextTemplating\", \"17.0.0.0\")]\n    public partial class MessagePackResolverTemplate : MessagePackResolverTemplateBase\n    {\n        /// <summary>\n        /// Create the template output\n        /// </summary>\n        public virtual string TransformText()\n        {\n            this.Write(\"// <auto-generated />\\r\\n#pragma warning disable\\r\\n#nullable enable\\r\\n\\r\\n\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(Using));\n            this.Write(\"\\r\\n\\r\\nnamespace \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(Namespace));\n            this.Write(\"\\r\\n{\\r\\n    public class \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(ClassName));\n            this.Write(\" : global::MessagePack.IFormatterResolver\\r\\n    {\\r\\n        public static readonly \" +\n                    \"global::MessagePack.IFormatterResolver Instance = new \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(ClassName));\n            this.Write(\"();\\r\\n\\r\\n        \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(ClassName));\n            this.Write(@\"()\n        {\n\n        }\n\n        public global::MessagePack.Formatters.IMessagePackFormatter<T>? GetFormatter<T>()\n        {\n            return FormatterCache<T>.formatter;\n        }\n\n        static class FormatterCache<T>\n        {\n            public static readonly global::MessagePack.Formatters.IMessagePackFormatter<T>? formatter;\n\n            static FormatterCache()\n            {\n                var f = \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(ClassName));\n            this.Write(@\"GetFormatterHelper.GetFormatter(typeof(T));\n                if (f != null)\n                {\n                    formatter = (global::MessagePack.Formatters.IMessagePackFormatter<T>)f;\n                }\n            }\n        }\n    }\n\n    internal static class \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(ClassName));\n            this.Write(\"GetFormatterHelper\\r\\n    {\\r\\n        static readonly global::System.Collections.Gen\" +\n                    \"eric.Dictionary<Type, int> lookup;\\r\\n\\r\\n        static \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(ClassName));\n            this.Write(\"GetFormatterHelper()\\r\\n        {\\r\\n            lookup = new global::System.Collecti\" +\n                    \"ons.Generic.Dictionary<Type, int>(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContexts.Length));\n            this.Write(\")\\r\\n            {\\r\\n\");\n for(var i = 0; i < GenerationContexts.Length; i++) { var item = GenerationContexts[i]; \n            this.Write(\"                {typeof(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\"[]), \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(i));\n            this.Write(\" },\\r\\n\");\n } \n            this.Write(\"            };\\r\\n        }\\r\\n\\r\\n        internal static object? GetFormatter(Type t)\" +\n                    \"\\r\\n        {\\r\\n            int key;\\r\\n            if (!lookup.TryGetValue(t, out ke\" +\n                    \"y)) return null;\\r\\n\\r\\n            switch (key)\\r\\n            {\\r\\n\");\n for(var i = 0; i < GenerationContexts.Length; i++) { var item = GenerationContexts[i]; \n            this.Write(\"                case \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(i));\n            this.Write(\": return new MessagePack.Formatters.ArrayFormatter<\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.ClassName));\n            this.Write(\">();\\r\\n\");\n } \n            this.Write(\"                default: return null;\\r\\n            }\\r\\n        }\\r\\n    }\\r\\n}\");\n            return this.GenerationEnvironment.ToString();\n        }\n    }\n    #region Base class\n    /// <summary>\n    /// Base class for this transformation\n    /// </summary>\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.TextTemplating\", \"17.0.0.0\")]\n    public class MessagePackResolverTemplateBase\n    {\n        #region Fields\n        private global::System.Text.StringBuilder generationEnvironmentField;\n        private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField;\n        private global::System.Collections.Generic.List<int> indentLengthsField;\n        private string currentIndentField = \"\";\n        private bool endsWithNewline;\n        private global::System.Collections.Generic.IDictionary<string, object> sessionField;\n        #endregion\n        #region Properties\n        /// <summary>\n        /// The string builder that generation-time code is using to assemble generated output\n        /// </summary>\n        public System.Text.StringBuilder GenerationEnvironment\n        {\n            get\n            {\n                if ((this.generationEnvironmentField == null))\n                {\n                    this.generationEnvironmentField = new global::System.Text.StringBuilder();\n                }\n                return this.generationEnvironmentField;\n            }\n            set\n            {\n                this.generationEnvironmentField = value;\n            }\n        }\n        /// <summary>\n        /// The error collection for the generation process\n        /// </summary>\n        public System.CodeDom.Compiler.CompilerErrorCollection Errors\n        {\n            get\n            {\n                if ((this.errorsField == null))\n                {\n                    this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection();\n                }\n                return this.errorsField;\n            }\n        }\n        /// <summary>\n        /// A list of the lengths of each indent that was added with PushIndent\n        /// </summary>\n        private System.Collections.Generic.List<int> indentLengths\n        {\n            get\n            {\n                if ((this.indentLengthsField == null))\n                {\n                    this.indentLengthsField = new global::System.Collections.Generic.List<int>();\n                }\n                return this.indentLengthsField;\n            }\n        }\n        /// <summary>\n        /// Gets the current indent we use when adding lines to the output\n        /// </summary>\n        public string CurrentIndent\n        {\n            get\n            {\n                return this.currentIndentField;\n            }\n        }\n        /// <summary>\n        /// Current transformation session\n        /// </summary>\n        public virtual global::System.Collections.Generic.IDictionary<string, object> Session\n        {\n            get\n            {\n                return this.sessionField;\n            }\n            set\n            {\n                this.sessionField = value;\n            }\n        }\n        #endregion\n        #region Transform-time helpers\n        /// <summary>\n        /// Write text directly into the generated output\n        /// </summary>\n        public void Write(string textToAppend)\n        {\n            if (string.IsNullOrEmpty(textToAppend))\n            {\n                return;\n            }\n            // If we're starting off, or if the previous text ended with a newline,\n            // we have to append the current indent first.\n            if (((this.GenerationEnvironment.Length == 0) \n                        || this.endsWithNewline))\n            {\n                this.GenerationEnvironment.Append(this.currentIndentField);\n                this.endsWithNewline = false;\n            }\n            // Check if the current text ends with a newline\n            if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture))\n            {\n                this.endsWithNewline = true;\n            }\n            // This is an optimization. If the current indent is \"\", then we don't have to do any\n            // of the more complex stuff further down.\n            if ((this.currentIndentField.Length == 0))\n            {\n                this.GenerationEnvironment.Append(textToAppend);\n                return;\n            }\n            // Everywhere there is a newline in the text, add an indent after it\n            textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField));\n            // If the text ends with a newline, then we should strip off the indent added at the very end\n            // because the appropriate indent will be added when the next time Write() is called\n            if (this.endsWithNewline)\n            {\n                this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length));\n            }\n            else\n            {\n                this.GenerationEnvironment.Append(textToAppend);\n            }\n        }\n        /// <summary>\n        /// Write text directly into the generated output\n        /// </summary>\n        public void WriteLine(string textToAppend)\n        {\n            this.Write(textToAppend);\n            this.GenerationEnvironment.AppendLine();\n            this.endsWithNewline = true;\n        }\n        /// <summary>\n        /// Write formatted text directly into the generated output\n        /// </summary>\n        public void Write(string format, params object[] args)\n        {\n            this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));\n        }\n        /// <summary>\n        /// Write formatted text directly into the generated output\n        /// </summary>\n        public void WriteLine(string format, params object[] args)\n        {\n            this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));\n        }\n        /// <summary>\n        /// Raise an error\n        /// </summary>\n        public void Error(string message)\n        {\n            System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();\n            error.ErrorText = message;\n            this.Errors.Add(error);\n        }\n        /// <summary>\n        /// Raise a warning\n        /// </summary>\n        public void Warning(string message)\n        {\n            System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();\n            error.ErrorText = message;\n            error.IsWarning = true;\n            this.Errors.Add(error);\n        }\n        /// <summary>\n        /// Increase the indent\n        /// </summary>\n        public void PushIndent(string indent)\n        {\n            if ((indent == null))\n            {\n                throw new global::System.ArgumentNullException(\"indent\");\n            }\n            this.currentIndentField = (this.currentIndentField + indent);\n            this.indentLengths.Add(indent.Length);\n        }\n        /// <summary>\n        /// Remove the last indent that was added with PushIndent\n        /// </summary>\n        public string PopIndent()\n        {\n            string returnValue = \"\";\n            if ((this.indentLengths.Count > 0))\n            {\n                int indentLength = this.indentLengths[(this.indentLengths.Count - 1)];\n                this.indentLengths.RemoveAt((this.indentLengths.Count - 1));\n                if ((indentLength > 0))\n                {\n                    returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength));\n                    this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength));\n                }\n            }\n            return returnValue;\n        }\n        /// <summary>\n        /// Remove any indentation\n        /// </summary>\n        public void ClearIndent()\n        {\n            this.indentLengths.Clear();\n            this.currentIndentField = \"\";\n        }\n        #endregion\n        #region ToString Helpers\n        /// <summary>\n        /// Utility class to produce culture-oriented representation of an object as a string.\n        /// </summary>\n        public class ToStringInstanceHelper\n        {\n            private System.IFormatProvider formatProviderField  = global::System.Globalization.CultureInfo.InvariantCulture;\n            /// <summary>\n            /// Gets or sets format provider to be used by ToStringWithCulture method.\n            /// </summary>\n            public System.IFormatProvider FormatProvider\n            {\n                get\n                {\n                    return this.formatProviderField ;\n                }\n                set\n                {\n                    if ((value != null))\n                    {\n                        this.formatProviderField  = value;\n                    }\n                }\n            }\n            /// <summary>\n            /// This is called from the compile/run appdomain to convert objects within an expression block to a string\n            /// </summary>\n            public string ToStringWithCulture(object objectToConvert)\n            {\n                if ((objectToConvert == null))\n                {\n                    throw new global::System.ArgumentNullException(\"objectToConvert\");\n                }\n                System.Type t = objectToConvert.GetType();\n                System.Reflection.MethodInfo method = t.GetMethod(\"ToString\", new System.Type[] {\n                            typeof(System.IFormatProvider)});\n                if ((method == null))\n                {\n                    return objectToConvert.ToString();\n                }\n                else\n                {\n                    return ((string)(method.Invoke(objectToConvert, new object[] {\n                                this.formatProviderField })));\n                }\n            }\n        }\n        private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper();\n        /// <summary>\n        /// Helper to produce culture-oriented representation of an object as a string\n        /// </summary>\n        public ToStringInstanceHelper ToStringHelper\n        {\n            get\n            {\n                return this.toStringHelperField;\n            }\n        }\n        #endregion\n    }\n    #endregion\n}\n"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/GeneratorCore/MessagePackResolverTemplate.tt",
    "content": "﻿<#@ template debug=\"false\" hostspecific=\"false\" linePragmas=\"false\" language=\"C#\" #>\n<#@ assembly name=\"System.Core\" #>\n<#@ import namespace=\"System.Linq\" #>\n<#@ import namespace=\"System.Text\" #>\n<#@ import namespace=\"System.Collections.Generic\" #>\n// <auto-generated />\n#pragma warning disable\n#nullable enable\n\n<#= Using #>\n\nnamespace <#= Namespace #>\n{\n    public class <#= ClassName #> : global::MessagePack.IFormatterResolver\n    {\n        public static readonly global::MessagePack.IFormatterResolver Instance = new <#= ClassName #>();\n\n        <#= ClassName #>()\n        {\n\n        }\n\n        public global::MessagePack.Formatters.IMessagePackFormatter<T>? GetFormatter<T>()\n        {\n            return FormatterCache<T>.formatter;\n        }\n\n        static class FormatterCache<T>\n        {\n            public static readonly global::MessagePack.Formatters.IMessagePackFormatter<T>? formatter;\n\n            static FormatterCache()\n            {\n                var f = <#= ClassName #>GetFormatterHelper.GetFormatter(typeof(T));\n                if (f != null)\n                {\n                    formatter = (global::MessagePack.Formatters.IMessagePackFormatter<T>)f;\n                }\n            }\n        }\n    }\n\n    internal static class <#= ClassName #>GetFormatterHelper\n    {\n        static readonly global::System.Collections.Generic.Dictionary<Type, int> lookup;\n\n        static <#= ClassName #>GetFormatterHelper()\n        {\n            lookup = new global::System.Collections.Generic.Dictionary<Type, int>(<#= GenerationContexts.Length #>)\n            {\n<# for(var i = 0; i < GenerationContexts.Length; i++) { var item = GenerationContexts[i]; #>\n                {typeof(<#= item.ClassName #>[]), <#= i #> },\n<# } #>\n            };\n        }\n\n        internal static object? GetFormatter(Type t)\n        {\n            int key;\n            if (!lookup.TryGetValue(t, out key)) return null;\n\n            switch (key)\n            {\n<# for(var i = 0; i < GenerationContexts.Length; i++) { var item = GenerationContexts[i]; #>\n                case <#= i #>: return new MessagePack.Formatters.ArrayFormatter<<#= item.ClassName #>>();\n<# } #>\n                default: return null;\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/GeneratorCore/TableTemplate.cs",
    "content": "﻿// ------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version: 17.0.0.0\n//  \n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n// ------------------------------------------------------------------------------\nnamespace MasterMemory.GeneratorCore\n{\n    using System.Linq;\n    using System.Text;\n    using System.Collections.Generic;\n    using System;\n    \n    /// <summary>\n    /// Class to produce the template output\n    /// </summary>\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.TextTemplating\", \"17.0.0.0\")]\n    public partial class TableTemplate : TableTemplateBase\n    {\n        /// <summary>\n        /// Create the template output\n        /// </summary>\n        public virtual string TransformText()\n        {\n            this.Write(\"// <auto-generated />\\r\\n#pragma warning disable\\r\\n#nullable enable\\r\\n\\r\\n\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(Using));\n            this.Write(\"\\r\\n\\r\\nnamespace \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(Namespace));\n            this.Write(\".Tables\\r\\n{\\r\\n   public sealed partial class \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.ClassName));\n            this.Write(\"Table : TableBase<\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.ClassName));\n            this.Write(\">, ITableUniqueValidate\\r\\n   {\\r\\n        public Func<\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.ClassName));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.PrimaryKey.BuildTypeName()));\n            this.Write(\"> PrimaryKeySelector => \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.PrimaryKey.SelectorName));\n            this.Write(\";\\r\\n        readonly Func<\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.ClassName));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.PrimaryKey.BuildTypeName()));\n            this.Write(\"> \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.PrimaryKey.SelectorName));\n            this.Write(\";\\r\\n\\r\\n\");\n for(var i = 0; i < GenerationContext.SecondaryKeys.Length; i++) { var item = GenerationContext.SecondaryKeys[i]; \n            this.Write(\"        readonly \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.ClassName));\n            this.Write(\"[] \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.TableName));\n            this.Write(\";\\r\\n        readonly Func<\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.ClassName));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildTypeName()));\n            this.Write(\"> \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.SelectorName));\n            this.Write(\";\\r\\n\");\n } \n            this.Write(\"\\r\\n        public \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.ClassName));\n            this.Write(\"Table(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.ClassName));\n            this.Write(\"[] sortedData)\\r\\n            : base(sortedData)\\r\\n        {\\r\\n            this.\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.PrimaryKey.SelectorName));\n            this.Write(\" = x => \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.PrimaryKey.BuildKeyAccessor(\"x\")));\n            this.Write(\";\\r\\n\");\n for(var i = 0; i < GenerationContext.SecondaryKeys.Length; i++) { var item = GenerationContext.SecondaryKeys[i]; \n            this.Write(\"            this.\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.SelectorName));\n            this.Write(\" = x => \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildKeyAccessor(\"x\")));\n            this.Write(\";\\r\\n            this.\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.TableName));\n            this.Write(\" = CloneAndSortBy(this.secondaryIndex\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.IndexNo));\n            this.Write(\"Selector, \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildComparer()));\n            this.Write(\");\\r\\n\");\n } \n            this.Write(\"            OnAfterConstruct();\\r\\n        }\\r\\n\\r\\n        partial void OnAfterConstru\" +\n                    \"ct();\\r\\n\\r\\n\");\n for(var i = 0; i < GenerationContext.SecondaryKeys.Length; i++) { var item = GenerationContext.SecondaryKeys[i]; \n            this.Write(\"        public RangeView<\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.ClassName));\n            this.Write(\"> SortBy\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildMethodName()));\n            this.Write(\" => new RangeView<\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.ClassName));\n            this.Write(\">(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.TableName));\n            this.Write(\", 0, \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.TableName));\n            this.Write(\".Length - 1, true);\\r\\n\");\n } \n            this.Write(\"\\r\\n\");\n foreach(var item in new KeyBase[] { GenerationContext.PrimaryKey }.Concat(GenerationContext.SecondaryKeys)) { \n if(item.CanInlineBinarySearch) { \n            this.Write(\"        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServic\" +\n                    \"es.MethodImplOptions.AggressiveInlining)]\\r\\n        public \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildReturnTypeName(GenerationContext.ClassName) + (ThrowKeyIfNotFound ? \"\" : \"?\")));\n            this.Write(\" FindBy\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildMethodName()));\n            this.Write(\"(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildTypeName()));\n            this.Write(\" key)\\r\\n        {\\r\\n            var lo = 0;\\r\\n            var hi = data.Length - 1;\\r\" +\n                    \"\\n            while (lo <= hi)\\r\\n            {\\r\\n                var mid = (int)(((\" +\n                    \"uint)hi + (uint)lo) >> 1);\\r\\n                var selected = data[mid].\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.Properties[0].Name));\n            this.Write(\";\\r\\n                var found = (selected < key) ? -1 : (selected > key) ? 1 : 0;\\r\" +\n                    \"\\n                if (found == 0) { return data[mid]; }\\r\\n                if (foun\" +\n                    \"d < 0) { lo = mid + 1; }\\r\\n                else { hi = mid - 1; }\\r\\n            }\\r\" +\n                    \"\\n\");\n if(ThrowKeyIfNotFound) { \n            this.Write(\"            return ThrowKeyNotFound(key);\\r\\n\");\n } else { \n            this.Write(\"            return default;\\r\\n\");\n } \n            this.Write(\"        }\\r\\n\\r\\n        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.C\" +\n                    \"ompilerServices.MethodImplOptions.AggressiveInlining)]\\r\\n        public bool TryF\" +\n                    \"indBy\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildMethodName()));\n            this.Write(\"(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildTypeName()));\n            this.Write(\" key, out \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildReturnTypeName(GenerationContext.ClassName)));\n            this.Write(\" result)\\r\\n        {\\r\\n            var lo = 0;\\r\\n            var hi = data.Length - \" +\n                    \"1;\\r\\n            while (lo <= hi)\\r\\n            {\\r\\n                var mid = (int)\" +\n                    \"(((uint)hi + (uint)lo) >> 1);\\r\\n                var selected = data[mid].\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.Properties[0].Name));\n            this.Write(@\";\n                var found = (selected < key) ? -1 : (selected > key) ? 1 : 0;\n                if (found == 0) { result = data[mid]; return true; }\n                if (found < 0) { lo = mid + 1; }\n                else { hi = mid - 1; }\n            }\n            result = default!;\n            return false;\n        }\n\");\n } else { \n if (!item.IsNonUnique) { \n            this.Write(\"        public \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildReturnTypeName(GenerationContext.ClassName)));\n            this.Write(\" FindBy\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildMethodName()));\n            this.Write(\"(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildTypeName()));\n            this.Write(\" key)\\r\\n        {\\r\\n            return \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildFindPrefix()));\n            this.Write(\"Core\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(!item.IsNonUnique && item.IsIntType ? \"Int\" : \"\"));\n            this.Write(\"(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.TableName));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.SelectorName));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildComparer()));\n            this.Write(\", key, \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(ThrowKeyIfNotFound.ToString().ToLower()));\n            this.Write(\");\\r\\n        }\\r\\n        \\r\\n        public bool TryFindBy\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildMethodName()));\n            this.Write(\"(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildTypeName()));\n            this.Write(\" key, out \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildReturnTypeName(GenerationContext.ClassName)));\n            this.Write(\" result)\\r\\n        {\\r\\n            return Try\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildFindPrefix()));\n            this.Write(\"Core\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(!item.IsNonUnique && item.IsIntType ? \"Int\" : \"\"));\n            this.Write(\"(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.TableName));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.SelectorName));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildComparer()));\n            this.Write(\", key, out result);\\r\\n        }\\r\\n\");\n } else { \n            this.Write(\"        public \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildReturnTypeName(GenerationContext.ClassName)));\n            this.Write(\" FindBy\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildMethodName()));\n            this.Write(\"(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildTypeName()));\n            this.Write(\" key)\\r\\n        {\\r\\n            return \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildFindPrefix()));\n            this.Write(\"Core\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(!item.IsNonUnique && item.IsIntType ? \"Int\" : \"\"));\n            this.Write(\"(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.TableName));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.SelectorName));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildComparer()));\n            this.Write(\", key);\\r\\n        }\\r\\n\");\n } \n } \n            this.Write(\"\\r\\n        public \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildReturnTypeNameForClosest(GenerationContext.ClassName)));\n            this.Write(\" FindClosestBy\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildMethodName()));\n            this.Write(\"(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildTypeName()));\n            this.Write(\" key, bool selectLower = true)\\r\\n        {\\r\\n            return \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildFindPrefix()));\n            this.Write(\"ClosestCore(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.TableName));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.SelectorName));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildComparer()));\n            this.Write(\", key, selectLower);\\r\\n        }\\r\\n\\r\\n        public RangeView<\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.ClassName));\n            this.Write(\"> FindRangeBy\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildMethodName()));\n            this.Write(\"(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildTypeName()));\n            this.Write(\" min, \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildTypeName()));\n            this.Write(\" max, bool ascendant = true)\\r\\n        {\\r\\n            return \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildFindPrefix()));\n            this.Write(\"RangeCore(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.TableName));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.SelectorName));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(item.BuildComparer()));\n            this.Write(\", min, max, ascendant);\\r\\n        }\\r\\n\\r\\n\");\n } \n            this.Write(\"\\r\\n        void ITableUniqueValidate.ValidateUnique(ValidateResult resultSet)\\r\\n   \" +\n                    \"     {\\r\\n#if !DISABLE_MASTERMEMORY_VALIDATOR\\r\\n\\r\\n\");\n if (!GenerationContext.PrimaryKey.IsNonUnique) { var key = GenerationContext.PrimaryKey; \n            this.Write(\"            ValidateUniqueCore(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(key.TableName));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(key.SelectorName));\n            this.Write(\", \\\"\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(key.BuildPropertyTupleName()));\n            this.Write(\"\\\", resultSet);       \\r\\n\");\n } \n for(var i = 0; i < GenerationContext.SecondaryKeys.Length; i++) { var key = GenerationContext.SecondaryKeys[i]; \n      if (!key.IsNonUnique) { \n            this.Write(\"            ValidateUniqueCore(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(key.TableName));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(key.SelectorName));\n            this.Write(\", \\\"\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(key.BuildPropertyTupleName()));\n            this.Write(\"\\\", resultSet);       \\r\\n\");\n      } \n } \n            this.Write(\"\\r\\n#endif\\r\\n        }\\r\\n\\r\\n#if !DISABLE_MASTERMEMORY_METADATABASE\\r\\n\\r\\n        public s\" +\n                    \"tatic MasterMemory.Meta.MetaTable CreateMetaTable()\\r\\n        {\\r\\n            retu\" +\n                    \"rn new MasterMemory.Meta.MetaTable(typeof(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.ClassName));\n            this.Write(\"), typeof(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.ClassName));\n            this.Write(\"Table), \\\"\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.MemoryTableName));\n            this.Write(\"\\\",\\r\\n                new MasterMemory.Meta.MetaProperty[]\\r\\n                {\\r\\n\");\n foreach(var prop in GenerationContext.Properties) { \n            this.Write(\"                    new MasterMemory.Meta.MetaProperty(typeof(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.ClassName));\n            this.Write(\").GetProperty(\\\"\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(prop.Name));\n            this.Write(\"\\\")!),\\r\\n\");\n } \n            this.Write(\"                },\\r\\n                new MasterMemory.Meta.MetaIndex[]{\\r\\n\");\n foreach(var key in GenerationContext.Keys) { \n            this.Write(\"                    new MasterMemory.Meta.MetaIndex(new System.Reflection.Propert\" +\n                    \"yInfo[] {\\r\\n\");\n foreach(var keyProp in key.Properties) { \n            this.Write(\"                        typeof(\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(GenerationContext.ClassName));\n            this.Write(\").GetProperty(\\\"\");\n            this.Write(this.ToStringHelper.ToStringWithCulture(keyProp.Name));\n            this.Write(\"\\\")!,\\r\\n\");\n } \n            this.Write(\"                    }, \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(key.IsPrimary.ToString().ToLower()));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture((!key.IsNonUnique).ToString().ToLower()));\n            this.Write(\", \");\n            this.Write(this.ToStringHelper.ToStringWithCulture(key.BuildComparer()));\n            this.Write(\"),\\r\\n\");\n } \n            this.Write(\"                });\\r\\n        }\\r\\n\\r\\n#endif\\r\\n    }\\r\\n}\");\n            return this.GenerationEnvironment.ToString();\n        }\n    }\n    #region Base class\n    /// <summary>\n    /// Base class for this transformation\n    /// </summary>\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.TextTemplating\", \"17.0.0.0\")]\n    public class TableTemplateBase\n    {\n        #region Fields\n        private global::System.Text.StringBuilder generationEnvironmentField;\n        private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField;\n        private global::System.Collections.Generic.List<int> indentLengthsField;\n        private string currentIndentField = \"\";\n        private bool endsWithNewline;\n        private global::System.Collections.Generic.IDictionary<string, object> sessionField;\n        #endregion\n        #region Properties\n        /// <summary>\n        /// The string builder that generation-time code is using to assemble generated output\n        /// </summary>\n        public System.Text.StringBuilder GenerationEnvironment\n        {\n            get\n            {\n                if ((this.generationEnvironmentField == null))\n                {\n                    this.generationEnvironmentField = new global::System.Text.StringBuilder();\n                }\n                return this.generationEnvironmentField;\n            }\n            set\n            {\n                this.generationEnvironmentField = value;\n            }\n        }\n        /// <summary>\n        /// The error collection for the generation process\n        /// </summary>\n        public System.CodeDom.Compiler.CompilerErrorCollection Errors\n        {\n            get\n            {\n                if ((this.errorsField == null))\n                {\n                    this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection();\n                }\n                return this.errorsField;\n            }\n        }\n        /// <summary>\n        /// A list of the lengths of each indent that was added with PushIndent\n        /// </summary>\n        private System.Collections.Generic.List<int> indentLengths\n        {\n            get\n            {\n                if ((this.indentLengthsField == null))\n                {\n                    this.indentLengthsField = new global::System.Collections.Generic.List<int>();\n                }\n                return this.indentLengthsField;\n            }\n        }\n        /// <summary>\n        /// Gets the current indent we use when adding lines to the output\n        /// </summary>\n        public string CurrentIndent\n        {\n            get\n            {\n                return this.currentIndentField;\n            }\n        }\n        /// <summary>\n        /// Current transformation session\n        /// </summary>\n        public virtual global::System.Collections.Generic.IDictionary<string, object> Session\n        {\n            get\n            {\n                return this.sessionField;\n            }\n            set\n            {\n                this.sessionField = value;\n            }\n        }\n        #endregion\n        #region Transform-time helpers\n        /// <summary>\n        /// Write text directly into the generated output\n        /// </summary>\n        public void Write(string textToAppend)\n        {\n            if (string.IsNullOrEmpty(textToAppend))\n            {\n                return;\n            }\n            // If we're starting off, or if the previous text ended with a newline,\n            // we have to append the current indent first.\n            if (((this.GenerationEnvironment.Length == 0) \n                        || this.endsWithNewline))\n            {\n                this.GenerationEnvironment.Append(this.currentIndentField);\n                this.endsWithNewline = false;\n            }\n            // Check if the current text ends with a newline\n            if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture))\n            {\n                this.endsWithNewline = true;\n            }\n            // This is an optimization. If the current indent is \"\", then we don't have to do any\n            // of the more complex stuff further down.\n            if ((this.currentIndentField.Length == 0))\n            {\n                this.GenerationEnvironment.Append(textToAppend);\n                return;\n            }\n            // Everywhere there is a newline in the text, add an indent after it\n            textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField));\n            // If the text ends with a newline, then we should strip off the indent added at the very end\n            // because the appropriate indent will be added when the next time Write() is called\n            if (this.endsWithNewline)\n            {\n                this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length));\n            }\n            else\n            {\n                this.GenerationEnvironment.Append(textToAppend);\n            }\n        }\n        /// <summary>\n        /// Write text directly into the generated output\n        /// </summary>\n        public void WriteLine(string textToAppend)\n        {\n            this.Write(textToAppend);\n            this.GenerationEnvironment.AppendLine();\n            this.endsWithNewline = true;\n        }\n        /// <summary>\n        /// Write formatted text directly into the generated output\n        /// </summary>\n        public void Write(string format, params object[] args)\n        {\n            this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));\n        }\n        /// <summary>\n        /// Write formatted text directly into the generated output\n        /// </summary>\n        public void WriteLine(string format, params object[] args)\n        {\n            this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));\n        }\n        /// <summary>\n        /// Raise an error\n        /// </summary>\n        public void Error(string message)\n        {\n            System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();\n            error.ErrorText = message;\n            this.Errors.Add(error);\n        }\n        /// <summary>\n        /// Raise a warning\n        /// </summary>\n        public void Warning(string message)\n        {\n            System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();\n            error.ErrorText = message;\n            error.IsWarning = true;\n            this.Errors.Add(error);\n        }\n        /// <summary>\n        /// Increase the indent\n        /// </summary>\n        public void PushIndent(string indent)\n        {\n            if ((indent == null))\n            {\n                throw new global::System.ArgumentNullException(\"indent\");\n            }\n            this.currentIndentField = (this.currentIndentField + indent);\n            this.indentLengths.Add(indent.Length);\n        }\n        /// <summary>\n        /// Remove the last indent that was added with PushIndent\n        /// </summary>\n        public string PopIndent()\n        {\n            string returnValue = \"\";\n            if ((this.indentLengths.Count > 0))\n            {\n                int indentLength = this.indentLengths[(this.indentLengths.Count - 1)];\n                this.indentLengths.RemoveAt((this.indentLengths.Count - 1));\n                if ((indentLength > 0))\n                {\n                    returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength));\n                    this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength));\n                }\n            }\n            return returnValue;\n        }\n        /// <summary>\n        /// Remove any indentation\n        /// </summary>\n        public void ClearIndent()\n        {\n            this.indentLengths.Clear();\n            this.currentIndentField = \"\";\n        }\n        #endregion\n        #region ToString Helpers\n        /// <summary>\n        /// Utility class to produce culture-oriented representation of an object as a string.\n        /// </summary>\n        public class ToStringInstanceHelper\n        {\n            private System.IFormatProvider formatProviderField  = global::System.Globalization.CultureInfo.InvariantCulture;\n            /// <summary>\n            /// Gets or sets format provider to be used by ToStringWithCulture method.\n            /// </summary>\n            public System.IFormatProvider FormatProvider\n            {\n                get\n                {\n                    return this.formatProviderField ;\n                }\n                set\n                {\n                    if ((value != null))\n                    {\n                        this.formatProviderField  = value;\n                    }\n                }\n            }\n            /// <summary>\n            /// This is called from the compile/run appdomain to convert objects within an expression block to a string\n            /// </summary>\n            public string ToStringWithCulture(object objectToConvert)\n            {\n                if ((objectToConvert == null))\n                {\n                    throw new global::System.ArgumentNullException(\"objectToConvert\");\n                }\n                System.Type t = objectToConvert.GetType();\n                System.Reflection.MethodInfo method = t.GetMethod(\"ToString\", new System.Type[] {\n                            typeof(System.IFormatProvider)});\n                if ((method == null))\n                {\n                    return objectToConvert.ToString();\n                }\n                else\n                {\n                    return ((string)(method.Invoke(objectToConvert, new object[] {\n                                this.formatProviderField })));\n                }\n            }\n        }\n        private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper();\n        /// <summary>\n        /// Helper to produce culture-oriented representation of an object as a string\n        /// </summary>\n        public ToStringInstanceHelper ToStringHelper\n        {\n            get\n            {\n                return this.toStringHelperField;\n            }\n        }\n        #endregion\n    }\n    #endregion\n}\n"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/GeneratorCore/TableTemplate.tt",
    "content": "﻿<#@ template debug=\"false\" hostspecific=\"false\" linePragmas=\"false\" language=\"C#\" #>\n<#@ assembly name=\"System.Core\" #>\n<#@ import namespace=\"System.Linq\" #>\n<#@ import namespace=\"System.Text\" #>\n<#@ import namespace=\"System.Collections.Generic\" #>\n// <auto-generated />\n#pragma warning disable\n#nullable enable\n\n<#= Using #>\n\nnamespace <#= Namespace #>.Tables\n{\n   public sealed partial class <#= GenerationContext.ClassName #>Table : TableBase<<#= GenerationContext.ClassName #>>, ITableUniqueValidate\n   {\n        public Func<<#= GenerationContext.ClassName #>, <#= GenerationContext.PrimaryKey.BuildTypeName() #>> PrimaryKeySelector => <#= GenerationContext.PrimaryKey.SelectorName #>;\n        readonly Func<<#= GenerationContext.ClassName #>, <#= GenerationContext.PrimaryKey.BuildTypeName() #>> <#= GenerationContext.PrimaryKey.SelectorName #>;\n\n<# for(var i = 0; i < GenerationContext.SecondaryKeys.Length; i++) { var item = GenerationContext.SecondaryKeys[i]; #>\n        readonly <#= GenerationContext.ClassName #>[] <#= item.TableName #>;\n        readonly Func<<#= GenerationContext.ClassName #>, <#= item.BuildTypeName() #>> <#= item.SelectorName #>;\n<# } #>\n\n        public <#= GenerationContext.ClassName #>Table(<#= GenerationContext.ClassName #>[] sortedData)\n            : base(sortedData)\n        {\n            this.<#= GenerationContext.PrimaryKey.SelectorName #> = x => <#= GenerationContext.PrimaryKey.BuildKeyAccessor(\"x\") #>;\n<# for(var i = 0; i < GenerationContext.SecondaryKeys.Length; i++) { var item = GenerationContext.SecondaryKeys[i]; #>\n            this.<#= item.SelectorName #> = x => <#= item.BuildKeyAccessor(\"x\") #>;\n            this.<#= item.TableName #> = CloneAndSortBy(this.secondaryIndex<#= item.IndexNo #>Selector, <#= item.BuildComparer() #>);\n<# } #>\n            OnAfterConstruct();\n        }\n\n        partial void OnAfterConstruct();\n\n<# for(var i = 0; i < GenerationContext.SecondaryKeys.Length; i++) { var item = GenerationContext.SecondaryKeys[i]; #>\n        public RangeView<<#= GenerationContext.ClassName #>> SortBy<#= item.BuildMethodName() #> => new RangeView<<#= GenerationContext.ClassName #>>(<#= item.TableName #>, 0, <#= item.TableName #>.Length - 1, true);\n<# } #>\n\n<# foreach(var item in new KeyBase[] { GenerationContext.PrimaryKey }.Concat(GenerationContext.SecondaryKeys)) { #>\n<# if(item.CanInlineBinarySearch) { #>\n        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]\n        public <#= item.BuildReturnTypeName(GenerationContext.ClassName) + (ThrowKeyIfNotFound ? \"\" : \"?\") #> FindBy<#= item.BuildMethodName() #>(<#= item.BuildTypeName() #> key)\n        {\n            var lo = 0;\n            var hi = data.Length - 1;\n            while (lo <= hi)\n            {\n                var mid = (int)(((uint)hi + (uint)lo) >> 1);\n                var selected = data[mid].<#= item.Properties[0].Name #>;\n                var found = (selected < key) ? -1 : (selected > key) ? 1 : 0;\n                if (found == 0) { return data[mid]; }\n                if (found < 0) { lo = mid + 1; }\n                else { hi = mid - 1; }\n            }\n<# if(ThrowKeyIfNotFound) { #>\n            return ThrowKeyNotFound(key);\n<# } else { #>\n            return default;\n<# } #>\n        }\n\n        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]\n        public bool TryFindBy<#= item.BuildMethodName() #>(<#= item.BuildTypeName() #> key, out <#= item.BuildReturnTypeName(GenerationContext.ClassName) #> result)\n        {\n            var lo = 0;\n            var hi = data.Length - 1;\n            while (lo <= hi)\n            {\n                var mid = (int)(((uint)hi + (uint)lo) >> 1);\n                var selected = data[mid].<#= item.Properties[0].Name #>;\n                var found = (selected < key) ? -1 : (selected > key) ? 1 : 0;\n                if (found == 0) { result = data[mid]; return true; }\n                if (found < 0) { lo = mid + 1; }\n                else { hi = mid - 1; }\n            }\n            result = default!;\n            return false;\n        }\n<# } else { #>\n<# if (!item.IsNonUnique) { #>\n        public <#= item.BuildReturnTypeName(GenerationContext.ClassName) #> FindBy<#= item.BuildMethodName() #>(<#= item.BuildTypeName() #> key)\n        {\n            return <#= item.BuildFindPrefix() #>Core<#= !item.IsNonUnique && item.IsIntType ? \"Int\" : \"\" #>(<#= item.TableName #>, <#= item.SelectorName #>, <#= item.BuildComparer() #>, key, <#= ThrowKeyIfNotFound.ToString().ToLower() #>);\n        }\n        \n        public bool TryFindBy<#= item.BuildMethodName() #>(<#= item.BuildTypeName() #> key, out <#= item.BuildReturnTypeName(GenerationContext.ClassName) #> result)\n        {\n            return Try<#= item.BuildFindPrefix() #>Core<#= !item.IsNonUnique && item.IsIntType ? \"Int\" : \"\" #>(<#= item.TableName #>, <#= item.SelectorName #>, <#= item.BuildComparer() #>, key, out result);\n        }\n<# } else { #>\n        public <#= item.BuildReturnTypeName(GenerationContext.ClassName) #> FindBy<#= item.BuildMethodName() #>(<#= item.BuildTypeName() #> key)\n        {\n            return <#= item.BuildFindPrefix() #>Core<#= !item.IsNonUnique && item.IsIntType ? \"Int\" : \"\" #>(<#= item.TableName #>, <#= item.SelectorName #>, <#= item.BuildComparer() #>, key);\n        }\n<# } #>\n<# } #>\n\n        public <#= item.BuildReturnTypeNameForClosest(GenerationContext.ClassName) #> FindClosestBy<#= item.BuildMethodName() #>(<#= item.BuildTypeName() #> key, bool selectLower = true)\n        {\n            return <#= item.BuildFindPrefix() #>ClosestCore(<#= item.TableName #>, <#= item.SelectorName #>, <#= item.BuildComparer() #>, key, selectLower);\n        }\n\n        public RangeView<<#= GenerationContext.ClassName #>> FindRangeBy<#= item.BuildMethodName() #>(<#= item.BuildTypeName() #> min, <#= item.BuildTypeName() #> max, bool ascendant = true)\n        {\n            return <#= item.BuildFindPrefix() #>RangeCore(<#= item.TableName #>, <#= item.SelectorName #>, <#= item.BuildComparer() #>, min, max, ascendant);\n        }\n\n<# } #>\n\n        void ITableUniqueValidate.ValidateUnique(ValidateResult resultSet)\n        {\n#if !DISABLE_MASTERMEMORY_VALIDATOR\n\n<# if (!GenerationContext.PrimaryKey.IsNonUnique) { var key = GenerationContext.PrimaryKey; #>\n            ValidateUniqueCore(<#= key.TableName #>, <#= key.SelectorName #>, \"<#= key.BuildPropertyTupleName() #>\", resultSet);       \n<# } #>\n<# for(var i = 0; i < GenerationContext.SecondaryKeys.Length; i++) { var key = GenerationContext.SecondaryKeys[i]; #>\n<#      if (!key.IsNonUnique) { #>\n            ValidateUniqueCore(<#= key.TableName #>, <#= key.SelectorName #>, \"<#= key.BuildPropertyTupleName() #>\", resultSet);       \n<#      } #>\n<# } #>\n\n#endif\n        }\n\n#if !DISABLE_MASTERMEMORY_METADATABASE\n\n        public static MasterMemory.Meta.MetaTable CreateMetaTable()\n        {\n            return new MasterMemory.Meta.MetaTable(typeof(<#= GenerationContext.ClassName #>), typeof(<#= GenerationContext.ClassName #>Table), \"<#= GenerationContext.MemoryTableName #>\",\n                new MasterMemory.Meta.MetaProperty[]\n                {\n<# foreach(var prop in GenerationContext.Properties) { #>\n                    new MasterMemory.Meta.MetaProperty(typeof(<#= GenerationContext.ClassName #>).GetProperty(\"<#= prop.Name #>\")!),\n<# } #>\n                },\n                new MasterMemory.Meta.MetaIndex[]{\n<# foreach(var key in GenerationContext.Keys) { #>\n                    new MasterMemory.Meta.MetaIndex(new System.Reflection.PropertyInfo[] {\n<# foreach(var keyProp in key.Properties) { #>\n                        typeof(<#= GenerationContext.ClassName #>).GetProperty(\"<#= keyProp.Name #>\")!,\n<# } #>\n                    }, <#= key.IsPrimary.ToString().ToLower() #>, <#= (!key.IsNonUnique).ToString().ToLower() #>, <#= key.BuildComparer() #>),\n<# } #>\n                });\n        }\n\n#endif\n    }\n}"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/GeneratorCore/Template.cs",
    "content": "﻿#nullable disable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace MasterMemory.GeneratorCore\n{\n    public partial class DatabaseBuilderTemplate\n    {\n        public string Namespace { get; set; }\n        public string Using { get; set; }\n        public string PrefixClassName { get; set; }\n        public GenerationContext[] GenerationContexts { get; set; }\n\n        public string ClassName => PrefixClassName + \"DatabaseBuilder\";\n    }\n\n    public partial class MemoryDatabaseTemplate\n    {\n        public string Namespace { get; set; }\n        public string Using { get; set; }\n        public string PrefixClassName { get; set; }\n        public GenerationContext[] GenerationContexts { get; set; }\n        public string ClassName => PrefixClassName + \"MemoryDatabase\";\n    }\n\n    public partial class MetaMemoryDatabaseTemplate\n    {\n        public string Namespace { get; set; }\n        public string Using { get; set; }\n        public string PrefixClassName { get; set; }\n        public GenerationContext[] GenerationContexts { get; set; }\n        public string ClassName => PrefixClassName + \"MetaMemoryDatabase\";\n    }\n\n    public partial class ImmutableBuilderTemplate\n    {\n        public string Namespace { get; set; }\n        public string Using { get; set; }\n        public string PrefixClassName { get; set; }\n        public GenerationContext[] GenerationContexts { get; set; }\n        public string ClassName => PrefixClassName + \"ImmutableBuilder\";\n    }\n\n    public partial class MessagePackResolverTemplate\n    {\n        public string Namespace { get; set; }\n        public string Using { get; set; }\n        public string PrefixClassName { get; set; }\n        public GenerationContext[] GenerationContexts { get; set; }\n        public string ClassName => PrefixClassName + \"MasterMemoryResolver\";\n    }\n\n    public partial class TableTemplate\n    {\n        public string Namespace { get; set; }\n        public string Using { get; set; }\n        public string PrefixClassName { get; set; }\n        public GenerationContext GenerationContext { get; set; }\n\n        public bool ThrowKeyIfNotFound { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/MasterMemory.SourceGenerator.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFramework>netstandard2.0</TargetFramework>\n\t\t<LangVersion>13</LangVersion>\n\t\t<ImplicitUsings>enable</ImplicitUsings>\n\t\t<Nullable>enable</Nullable>\n\t\t<IsRoslynComponent>true</IsRoslynComponent>\n\t\t<AnalyzerLanguage>cs</AnalyzerLanguage>\n\t\t<RootNamespace>MasterMemory</RootNamespace>\n\n\t\t<!-- NuGet packaging including in MasterMemory -->\n\t\t<IsPackable>false</IsPackable>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<!-- Use 4.3.0 to support Unity -->\n\t\t<PackageReference Include=\"Microsoft.CodeAnalysis.CSharp\" Version=\"4.3.0\" />\n\t\t<PackageReference Include=\"PolySharp\" Version=\"1.15.0\">\n\t\t\t<PrivateAssets>all</PrivateAssets>\n\t\t\t<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n\t\t</PackageReference>\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<None Update=\"GeneratorCore\\DatabaseBuilderTemplate.tt\">\n\t\t\t<Generator>TextTemplatingFilePreprocessor</Generator>\n\t\t\t<LastGenOutput>DatabaseBuilderTemplate.cs</LastGenOutput>\n\t\t</None>\n\t\t<None Update=\"GeneratorCore\\ImmutableBuilderTemplate.tt\">\n\t\t\t<Generator>TextTemplatingFilePreprocessor</Generator>\n\t\t\t<LastGenOutput>ImmutableBuilderTemplate.cs</LastGenOutput>\n\t\t</None>\n\t\t<None Update=\"GeneratorCore\\MemoryDatabaseTemplate.tt\">\n\t\t\t<Generator>TextTemplatingFilePreprocessor</Generator>\n\t\t\t<LastGenOutput>MemoryDatabaseTemplate.cs</LastGenOutput>\n\t\t</None>\n\t\t<None Update=\"GeneratorCore\\MessagePackResolverTemplate.tt\">\n\t\t\t<Generator>TextTemplatingFilePreprocessor</Generator>\n\t\t\t<LastGenOutput>MessagePackResolverTemplate.cs</LastGenOutput>\n\t\t</None>\n\t\t<None Update=\"GeneratorCore\\TableTemplate.tt\">\n\t\t\t<Generator>TextTemplatingFilePreprocessor</Generator>\n\t\t\t<LastGenOutput>TableTemplate.cs</LastGenOutput>\n\t\t</None>\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<Service Include=\"{508349b6-6b84-4df5-91f0-309beebad82d}\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<Compile Update=\"GeneratorCore\\DatabaseBuilderTemplate.cs\">\n\t\t\t<DesignTime>True</DesignTime>\n\t\t\t<AutoGen>True</AutoGen>\n\t\t\t<DependentUpon>DatabaseBuilderTemplate.tt</DependentUpon>\n\t\t</Compile>\n\t\t<Compile Update=\"GeneratorCore\\ImmutableBuilderTemplate.cs\">\n\t\t\t<DesignTime>True</DesignTime>\n\t\t\t<AutoGen>True</AutoGen>\n\t\t\t<DependentUpon>ImmutableBuilderTemplate.tt</DependentUpon>\n\t\t</Compile>\n\t\t<Compile Update=\"GeneratorCore\\MemoryDatabaseTemplate.cs\">\n\t\t\t<DesignTime>True</DesignTime>\n\t\t\t<AutoGen>True</AutoGen>\n\t\t\t<DependentUpon>MemoryDatabaseTemplate.tt</DependentUpon>\n\t\t</Compile>\n\t\t<Compile Update=\"GeneratorCore\\MessagePackResolverTemplate.cs\">\n\t\t\t<DesignTime>True</DesignTime>\n\t\t\t<AutoGen>True</AutoGen>\n\t\t\t<DependentUpon>MessagePackResolverTemplate.tt</DependentUpon>\n\t\t</Compile>\n\t\t<Compile Update=\"GeneratorCore\\TableTemplate.cs\">\n\t\t\t<DesignTime>True</DesignTime>\n\t\t\t<AutoGen>True</AutoGen>\n\t\t\t<DependentUpon>TableTemplate.tt</DependentUpon>\n\t\t</Compile>\n\t</ItemGroup>\n</Project>\n"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/MasterMemoryGenerator.cs",
    "content": "﻿using MasterMemory.GeneratorCore;\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing System.Diagnostics;\n\nnamespace MasterMemory.SourceGenerator;\n\n[Generator(LanguageNames.CSharp)]\npublic partial class MasterMemoryGenerator : IIncrementalGenerator\n{\n    public void Initialize(IncrementalGeneratorInitializationContext context)\n    {\n        context.RegisterPostInitializationOutput(MasterMemoryGeneratorOptions.EmitAttribute);\n\n        var namespaceProvider = context.AnalyzerConfigOptionsProvider.Select((x, _) =>\n            {\n                x.GlobalOptions.TryGetValue(\"build_property.RootNamespace\", out var defaultNamespace);\n                return defaultNamespace;\n            })\n            .WithTrackingName(\"MasterMemory.AnalyzerConfig\");\n\n        var generatorOptions = context.CompilationProvider.Select((compilation, _) =>\n            {\n                foreach (var attr in compilation.Assembly.GetAttributes())\n                {\n                    if (attr.AttributeClass?.Name == \"MasterMemoryGeneratorOptionsAttribute\")\n                    {\n                        return MasterMemoryGeneratorOptions.FromAttribute(attr);\n                    }\n                }\n\n                return default;\n            })\n            .WithTrackingName(\"MasterMemory.CompilationProvider\");\n\n        var memoryTables = context.SyntaxProvider.ForAttributeWithMetadataName(\"MasterMemory.MemoryTableAttribute\",\n            (node, token) => true,\n            (ctx, token) => ctx)\n            .WithTrackingName(\"MasterMemory.SyntaxProvider.0_ForAttributeWithMetadataName\")\n            .Collect()\n            .Select((xs, _) =>\n            {\n                var list = new List<GenerationContext>();\n                var reporter = new DiagnosticReporter();\n                foreach (var ctx in xs)\n                {\n                    var memoryTableAttr = ctx.Attributes[0]; // AllowMultiple=false\n                    var classDecl = ctx.TargetNode as TypeDeclarationSyntax; // class or record\n                    var context = CodeGenerator.CreateGenerationContext(classDecl!, memoryTableAttr, reporter);\n                    if (context != null)\n                    {\n                        list.Add(context);\n                    }\n                }\n                list.Sort((a, b) => string.Compare(a.ClassName, b.ClassName, StringComparison.Ordinal));\n                return (reporter, new EquatableArray<GenerationContext>(list.ToArray()));\n            })\n            .WithTrackingName(\"MasterMemory.SyntaxProvider.1_CollectAndSelect\");\n\n        var allCombined = memoryTables\n            .Combine(namespaceProvider)\n            .Combine(generatorOptions)\n            .WithTrackingName(\"MasterMemory.SyntaxProvider.2_AllCombined\");\n\n        context.RegisterSourceOutput(allCombined, EmitMemoryTable);\n    }\n\n    void EmitMemoryTable(SourceProductionContext context, (((DiagnosticReporter, EquatableArray<GenerationContext>), string?), MasterMemoryGeneratorOptions) value)\n    {\n        var (((diagnostic, memoryTables), defaultNamespace), generatorOptions) = value;\n        diagnostic.ReportToContext(context);\n        if (memoryTables.Length == 0)\n        {\n            return;\n        }\n\n        var usingNamespace = generatorOptions.Namespace ?? defaultNamespace ?? \"MasterMemory\";\n        var prefixClassName = generatorOptions.PrefixClassName ?? \"\";\n        var throwIfKeyNotFound = !generatorOptions.IsReturnNullIfKeyNotFound; // becareful, reverse!\n\n        var usingStrings = string.Join(Environment.NewLine, memoryTables.SelectMany(x => x.UsingStrings).Distinct().OrderBy(x => x, StringComparer.Ordinal));\n\n        var builderTemplate = new DatabaseBuilderTemplate();\n        var databaseTemplate = new MemoryDatabaseTemplate();\n        var immutableBuilderTemplate = new ImmutableBuilderTemplate();\n        var resolverTemplate = new MessagePackResolverTemplate();\n        builderTemplate.Namespace = databaseTemplate.Namespace = immutableBuilderTemplate.Namespace = resolverTemplate.Namespace = usingNamespace;\n        builderTemplate.PrefixClassName = databaseTemplate.PrefixClassName = immutableBuilderTemplate.PrefixClassName = resolverTemplate.PrefixClassName = prefixClassName;\n        builderTemplate.Using = databaseTemplate.Using = immutableBuilderTemplate.Using = resolverTemplate.Using = (usingStrings + Environment.NewLine + (\"using \" + usingNamespace + \".Tables;\"));\n        builderTemplate.GenerationContexts = databaseTemplate.GenerationContexts = immutableBuilderTemplate.GenerationContexts = resolverTemplate.GenerationContexts = memoryTables.ToArray();\n\n        Log(AddSource(context, builderTemplate.ClassName, builderTemplate.TransformText()));\n        Log(AddSource(context, immutableBuilderTemplate.ClassName, immutableBuilderTemplate.TransformText()));\n        Log(AddSource(context, databaseTemplate.ClassName, databaseTemplate.TransformText()));\n        Log(AddSource(context, resolverTemplate.ClassName, resolverTemplate.TransformText()));\n\n        foreach (var generationContext in memoryTables)\n        {\n            var template = new TableTemplate()\n            {\n                Namespace = usingNamespace,\n                GenerationContext = generationContext,\n                Using = string.Join(Environment.NewLine, generationContext.UsingStrings),\n                ThrowKeyIfNotFound = throwIfKeyNotFound\n            };\n\n            Log(AddSource(context, generationContext.ClassName + \"Table\", template.TransformText()));\n        }\n    }\n\n    static void Log(string msg) => Trace.WriteLine(msg);\n\n    static string AddSource(SourceProductionContext context, string fileName, string content)\n    {\n        var contentString = NormalizeNewLines(content);\n        context.AddSource($\"MasterMemory.{fileName}.g.cs\", contentString);\n        return $\"Generate {fileName}.\";\n\n        static string NormalizeNewLines(string content)\n        {\n            // The T4 generated code may be text with mixed line ending types. (CR + CRLF)\n            // We need to normalize the line ending type in each Operating Systems. (e.g. Windows=CRLF, Linux/macOS=LF)\n            return content.Replace(\"\\r\\n\", \"\\n\").Replace(\"\\n\", Environment.NewLine);\n        }\n    }\n}\n"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/MasterMemoryGeneratorOptions.cs",
    "content": "﻿using Microsoft.CodeAnalysis;\n\n\nnamespace MasterMemory.SourceGenerator;\n\nreadonly record struct MasterMemoryGeneratorOptions(string? Namespace, string PrefixClassName, bool IsReturnNullIfKeyNotFound)\n{\n    public static MasterMemoryGeneratorOptions FromAttribute(AttributeData attributeData)\n    {\n        var args = attributeData.NamedArguments;\n\n        var ns = args.FirstOrDefault(x => x.Key == nameof(Namespace)).Value.Value as string ?? null;\n        var prefix = args.FirstOrDefault(x => x.Key == nameof(PrefixClassName)).Value.Value as string ?? \"\";\n        var isReturnNull = args.FirstOrDefault(x => x.Key == nameof(IsReturnNullIfKeyNotFound)).Value.Value as bool? ?? null;\n\n        return new MasterMemoryGeneratorOptions(ns, prefix, isReturnNull ?? false);\n    }\n\n    public static void EmitAttribute(IncrementalGeneratorPostInitializationContext context)\n    {\n        context.AddSource(\"MasterMemory.MasterMemoryGeneratorOptions.g.cs\", \"\"\"\n// <auto-generated />\n#pragma warning disable\n#nullable enable\n\nusing System;\n\nnamespace MasterMemory\n{\n    [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)]\n    internal sealed class MasterMemoryGeneratorOptionsAttribute : Attribute\n    {\n        public string? Namespace { get; set; } = null;\n        public string PrefixClassName { get; set; } = \"\";\n        public bool IsReturnNullIfKeyNotFound { get; set; } = false;\n    }\n}\n\"\"\");\n    }\n}\n"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/Polyfill/System.CodeDom.cs",
    "content": "﻿#nullable disable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace System.CodeDom.Compiler\n{\n\tpublic class CompilerError\n\t{\n\t\tpublic string ErrorText { get; set; }\n\t\tpublic bool IsWarning { get; set; }\n\t}\n\n\tpublic class CompilerErrorCollection\n\t{\n\t\tpublic void Add(CompilerError error)\n\t\t{\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/Properties/launchSettings.json",
    "content": "{\n  \"profiles\": {\n    \"Profile 1\": {\n      \"commandName\": \"DebugRoslynComponent\",\n      \"targetProject\": \"..\\\\..\\\\sandbox\\\\GeneratorSandbox\\\\GeneratorSandbox.csproj\"\n    }\n  }\n}"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/Utility/EquatableArray.cs",
    "content": "﻿using System.Collections;\nusing System.Runtime.CompilerServices;\n\nnamespace MasterMemory;\n\npublic readonly struct EquatableArray<T> : IEquatable<EquatableArray<T>>, IEnumerable<T>\n    where T : IEquatable<T>\n{\n    readonly T[]? array;\n\n    public EquatableArray() // for collection literal []\n    {\n        array = [];\n    }\n\n    public EquatableArray(T[] array)\n    {\n        this.array = array;\n    }\n\n    public static implicit operator EquatableArray<T>(T[] array)\n    {\n        return new EquatableArray<T>(array);\n    }\n\n    public ref readonly T this[int index]\n    {\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        get => ref array![index];\n    }\n\n    public int Length => array!.Length;\n\n    public ReadOnlySpan<T> AsSpan()\n    {\n        return array.AsSpan();\n    }\n\n    public ReadOnlySpan<T>.Enumerator GetEnumerator()\n    {\n        return AsSpan().GetEnumerator();\n    }\n\n    IEnumerator<T> IEnumerable<T>.GetEnumerator()\n    {\n        return array.AsEnumerable().GetEnumerator();\n    }\n\n    IEnumerator IEnumerable.GetEnumerator()\n    {\n        return array.AsEnumerable().GetEnumerator();\n    }\n\n    public bool Equals(EquatableArray<T> other)\n    {\n        return AsSpan().SequenceEqual(other.AsSpan());\n    }\n}\n"
  },
  {
    "path": "src/MasterMemory.SourceGenerator/Utility/IgnoreEquality.cs",
    "content": "﻿namespace MasterMemory;\n\npublic readonly struct IgnoreEquality<T>(T value) : IEquatable<IgnoreEquality<T>>\n{\n    public readonly T Value => value;\n\n    public static implicit operator IgnoreEquality<T>(T value)\n    {\n        return new IgnoreEquality<T>(value);\n    }\n\n    public static implicit operator T(IgnoreEquality<T> value)\n    {\n        return value.Value;\n    }\n\n    public bool Equals(IgnoreEquality<T> other)\n    {\n        // always true to ignore equality check.\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/MasterMemory.Unity/Assets/NuGet.config",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <packageSources>\n    <clear />\n    <add key=\"nuget.org\" value=\"https://api.nuget.org/v3/index.json\" />\n  </packageSources>\n  <disabledPackageSources />\n  <activePackageSource>\n    <add key=\"All\" value=\"(Aggregate source)\" />\n  </activePackageSource>\n  <config>\n    <add key=\"packageInstallLocation\" value=\"CustomWithinAssets\" />\n    <add key=\"repositoryPath\" value=\"./Packages\" />\n    <add key=\"PackagesConfigDirectoryPath\" value=\".\" />\n    <add key=\"slimRestore\" value=\"true\" />\n    <add key=\"PreferNetStandardOverNetFramework\" value=\"true\" />\n  </config>\n</configuration>"
  },
  {
    "path": "src/MasterMemory.Unity/Assets/NuGet.config.meta",
    "content": "fileFormatVersion: 2\nguid: e5322b2ac44bca4478137f3076edc3bb\nlabels:\n- NuGetForUnity\nPluginImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  iconMap: {}\n  executionOrder: {}\n  defineConstraints: []\n  isPreloaded: 0\n  isOverridable: 0\n  isExplicitlyReferenced: 0\n  validateReferences: 1\n  platformData:\n  - first:\n      Any: \n    second:\n      enabled: 0\n      settings: {}\n  - first:\n      Editor: Editor\n    second:\n      enabled: 0\n      settings:\n        DefaultValueInitialized: true\n  - first:\n      Windows Store Apps: WindowsStoreApps\n    second:\n      enabled: 1\n      settings: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/MasterMemory.Unity/Assets/Packages.meta",
    "content": "fileFormatVersion: 2\nguid: 26358cf27391727439065c2117eb2e52\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/MasterMemory.Unity/Assets/Scenes/Main.unity",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!29 &1\nOcclusionCullingSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  m_OcclusionBakeSettings:\n    smallestOccluder: 5\n    smallestHole: 0.25\n    backfaceThreshold: 100\n  m_SceneGUID: 00000000000000000000000000000000\n  m_OcclusionCullingData: {fileID: 0}\n--- !u!104 &2\nRenderSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 9\n  m_Fog: 0\n  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}\n  m_FogMode: 3\n  m_FogDensity: 0.01\n  m_LinearFogStart: 0\n  m_LinearFogEnd: 300\n  m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}\n  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}\n  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}\n  m_AmbientIntensity: 1\n  m_AmbientMode: 3\n  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}\n  m_SkyboxMaterial: {fileID: 0}\n  m_HaloStrength: 0.5\n  m_FlareStrength: 1\n  m_FlareFadeSpeed: 3\n  m_HaloTexture: {fileID: 0}\n  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}\n  m_DefaultReflectionMode: 0\n  m_DefaultReflectionResolution: 128\n  m_ReflectionBounces: 1\n  m_ReflectionIntensity: 1\n  m_CustomReflection: {fileID: 0}\n  m_Sun: {fileID: 0}\n  m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}\n  m_UseRadianceAmbientProbe: 0\n--- !u!157 &3\nLightmapSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 12\n  m_GIWorkflowMode: 1\n  m_GISettings:\n    serializedVersion: 2\n    m_BounceScale: 1\n    m_IndirectOutputScale: 1\n    m_AlbedoBoost: 1\n    m_EnvironmentLightingMode: 0\n    m_EnableBakedLightmaps: 0\n    m_EnableRealtimeLightmaps: 0\n  m_LightmapEditorSettings:\n    serializedVersion: 12\n    m_Resolution: 2\n    m_BakeResolution: 40\n    m_AtlasSize: 1024\n    m_AO: 0\n    m_AOMaxDistance: 1\n    m_CompAOExponent: 1\n    m_CompAOExponentDirect: 0\n    m_ExtractAmbientOcclusion: 0\n    m_Padding: 2\n    m_LightmapParameters: {fileID: 0}\n    m_LightmapsBakeMode: 1\n    m_TextureCompression: 1\n    m_FinalGather: 0\n    m_FinalGatherFiltering: 1\n    m_FinalGatherRayCount: 256\n    m_ReflectionCompression: 2\n    m_MixedBakeMode: 2\n    m_BakeBackend: 1\n    m_PVRSampling: 1\n    m_PVRDirectSampleCount: 32\n    m_PVRSampleCount: 512\n    m_PVRBounces: 2\n    m_PVREnvironmentSampleCount: 256\n    m_PVREnvironmentReferencePointCount: 2048\n    m_PVRFilteringMode: 1\n    m_PVRDenoiserTypeDirect: 1\n    m_PVRDenoiserTypeIndirect: 1\n    m_PVRDenoiserTypeAO: 1\n    m_PVRFilterTypeDirect: 0\n    m_PVRFilterTypeIndirect: 0\n    m_PVRFilterTypeAO: 0\n    m_PVREnvironmentMIS: 1\n    m_PVRCulling: 1\n    m_PVRFilteringGaussRadiusDirect: 1\n    m_PVRFilteringGaussRadiusIndirect: 5\n    m_PVRFilteringGaussRadiusAO: 2\n    m_PVRFilteringAtrousPositionSigmaDirect: 0.5\n    m_PVRFilteringAtrousPositionSigmaIndirect: 2\n    m_PVRFilteringAtrousPositionSigmaAO: 1\n    m_ExportTrainingData: 0\n    m_TrainingDataDestination: TrainingData\n    m_LightProbeSampleCountMultiplier: 4\n  m_LightingDataAsset: {fileID: 0}\n  m_LightingSettings: {fileID: 0}\n--- !u!196 &4\nNavMeshSettings:\n  serializedVersion: 2\n  m_ObjectHideFlags: 0\n  m_BuildSettings:\n    serializedVersion: 3\n    agentTypeID: 0\n    agentRadius: 0.5\n    agentHeight: 2\n    agentSlope: 45\n    agentClimb: 0.4\n    ledgeDropHeight: 0\n    maxJumpAcrossDistance: 0\n    minRegionArea: 2\n    manualCellSize: 0\n    cellSize: 0.16666667\n    manualTileSize: 0\n    tileSize: 256\n    buildHeightMesh: 0\n    maxJobWorkers: 0\n    preserveTilesOutsideBounds: 0\n    debug:\n      m_Flags: 0\n  m_NavMeshData: {fileID: 0}\n--- !u!1 &242466054\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 242466057}\n  - component: {fileID: 242466056}\n  - component: {fileID: 242466055}\n  - component: {fileID: 242466058}\n  m_Layer: 0\n  m_Name: Main Camera\n  m_TagString: MainCamera\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!81 &242466055\nAudioListener:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 242466054}\n  m_Enabled: 1\n--- !u!20 &242466056\nCamera:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 242466054}\n  m_Enabled: 1\n  serializedVersion: 2\n  m_ClearFlags: 1\n  m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}\n  m_projectionMatrixMode: 1\n  m_GateFitMode: 2\n  m_FOVAxisMode: 0\n  m_Iso: 200\n  m_ShutterSpeed: 0.005\n  m_Aperture: 16\n  m_FocusDistance: 10\n  m_FocalLength: 50\n  m_BladeCount: 5\n  m_Curvature: {x: 2, y: 11}\n  m_BarrelClipping: 0.25\n  m_Anamorphism: 0\n  m_SensorSize: {x: 36, y: 24}\n  m_LensShift: {x: 0, y: 0}\n  m_NormalizedViewPortRect:\n    serializedVersion: 2\n    x: 0\n    y: 0\n    width: 1\n    height: 1\n  near clip plane: 0.3\n  far clip plane: 1000\n  field of view: 60\n  orthographic: 1\n  orthographic size: 5\n  m_Depth: -1\n  m_CullingMask:\n    serializedVersion: 2\n    m_Bits: 4294967295\n  m_RenderingPath: -1\n  m_TargetTexture: {fileID: 0}\n  m_TargetDisplay: 0\n  m_TargetEye: 3\n  m_HDR: 1\n  m_AllowMSAA: 1\n  m_AllowDynamicResolution: 0\n  m_ForceIntoRT: 0\n  m_OcclusionCulling: 1\n  m_StereoConvergence: 10\n  m_StereoSeparation: 0.022\n--- !u!4 &242466057\nTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 242466054}\n  serializedVersion: 2\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: -10}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 0}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n--- !u!114 &242466058\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 242466054}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: a7e2d905f1c43ee42914ee6de131c41e, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n--- !u!1660057539 &9223372036854775807\nSceneRoots:\n  m_ObjectHideFlags: 0\n  m_Roots:\n  - {fileID: 242466057}\n"
  },
  {
    "path": "src/MasterMemory.Unity/Assets/Scenes/Main.unity.meta",
    "content": "fileFormatVersion: 2\nguid: d5ed035d1a1185e43a6e9d45e3d68f1f\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/MasterMemory.Unity/Assets/Scenes.meta",
    "content": "fileFormatVersion: 2\nguid: 3e4672a57ce755a44805bc58b4ddea29\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/MasterMemory.Unity/Assets/Scripts/NewBehaviourScript.cs",
    "content": "using MasterMemory;\nusing MessagePack;\nusing MessagePack.Resolvers;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\nusing MyProj;\n\n[assembly: MasterMemoryGeneratorOptions(Namespace = \"MyProj\")]\n\n// If you want to use init, copy-and-paste this.\nnamespace System.Runtime.CompilerServices\n{\n    internal sealed class IsExternalInit { }\n}\n\n\n\npublic class NewBehaviourScript : MonoBehaviour\n{\n    // Start is called before the first frame update\n    void Start()\n    {\n\n    }\n\n    // Update is called once per frame\n    void Update()\n    {\n\n    }\n}\n\npublic enum Gender\n{\n    Male, Female, Unknown\n}\n\n// table definition marked by MemoryTableAttribute.\n// database-table must be serializable by MessagePack-CSsharp\n[MemoryTable(\"person\"), MessagePackObject(true)]\npublic record Person\n{\n    // index definition by attributes.\n    [PrimaryKey]\n    public int PersonId { get; init; }\n\n    // secondary index can add multiple(discriminated by index-number).\n    [SecondaryKey(0), NonUnique]\n    [SecondaryKey(1, keyOrder: 1), NonUnique]\n    public int Age { get; init; }\n\n    [SecondaryKey(2), NonUnique]\n    [SecondaryKey(1, keyOrder: 0), NonUnique]\n    public Gender Gender { get; init; }\n\n    public string Name { get; init; }\n}\n\npublic static class Initializer\n{\n    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]\n    public static void SetupMessagePackResolver()\n    {\n        // Create CompositeResolver\n        StaticCompositeResolver.Instance.Register(new[]{\n            MasterMemoryResolver.Instance, // set MasterMemory generated resolver\n            StandardResolver.Instance      // set default MessagePack resolver\n        });\n\n        // Set as default\n        var options = MessagePackSerializerOptions.Standard.WithResolver(StaticCompositeResolver.Instance);\n        MessagePackSerializer.DefaultOptions = options;\n    }\n}"
  },
  {
    "path": "src/MasterMemory.Unity/Assets/Scripts/NewBehaviourScript.cs.meta",
    "content": "fileFormatVersion: 2\nguid: a7e2d905f1c43ee42914ee6de131c41e\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/MasterMemory.Unity/Assets/Scripts.meta",
    "content": "fileFormatVersion: 2\nguid: dfc2745c192a6764a8f038393ed2455c\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/MasterMemory.Unity/Assets/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"MessagePack\" version=\"3.1.0\" manuallyInstalled=\"true\" />\n  <package id=\"MessagePack.Annotations\" version=\"3.1.0\" />\n  <package id=\"MessagePackAnalyzer\" version=\"3.1.0\" />\n  <package id=\"Microsoft.NET.StringTools\" version=\"17.11.4\" />\n  <package id=\"System.Collections.Immutable\" version=\"8.0.0\" />\n  <package id=\"System.Runtime.CompilerServices.Unsafe\" version=\"6.0.0\" />\n</packages>"
  },
  {
    "path": "src/MasterMemory.Unity/Assets/packages.config.meta",
    "content": "fileFormatVersion: 2\nguid: 44858e3667fc6e44c8fc19fd02574910\nlabels:\n- NuGetForUnity\nPluginImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  iconMap: {}\n  executionOrder: {}\n  defineConstraints: []\n  isPreloaded: 0\n  isOverridable: 0\n  isExplicitlyReferenced: 0\n  validateReferences: 1\n  platformData:\n  - first:\n      Any: \n    second:\n      enabled: 1\n      settings: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/MasterMemory.Unity/Packages/manifest.json",
    "content": "{\n  \"dependencies\": {\n    \"com.cysharp.runtimeunittesttoolkit\": \"https://github.com/Cysharp/RuntimeUnitTestToolkit.git?path=RuntimeUnitTestToolkit/Assets/RuntimeUnitTestToolkit#2.6.0\",\n    \"com.github-glitchenzo.nugetforunity\": \"https://github.com/GlitchEnzo/NuGetForUnity.git?path=/src/NuGetForUnity\",\n    \"com.github.mastermemory.internal\": \"file:/../../src/MasterMemory/bin/Debug/netstandard2.0\",\n    \"com.unity.ide.rider\": \"3.0.31\",\n    \"com.unity.ide.visualstudio\": \"2.0.22\",\n    \"com.unity.ide.vscode\": \"1.2.5\",\n    \"com.unity.test-framework\": \"1.1.33\",\n    \"com.unity.toolchain.win-x86_64-linux-x86_64\": \"2.0.9\",\n    \"com.unity.ugui\": \"1.0.0\"\n  }\n}\n"
  },
  {
    "path": "src/MasterMemory.Unity/Packages/packages-lock.json",
    "content": "{\n  \"dependencies\": {\n    \"com.cysharp.runtimeunittesttoolkit\": {\n      \"version\": \"https://github.com/Cysharp/RuntimeUnitTestToolkit.git?path=RuntimeUnitTestToolkit/Assets/RuntimeUnitTestToolkit#2.6.0\",\n      \"depth\": 0,\n      \"source\": \"git\",\n      \"dependencies\": {},\n      \"hash\": \"4e3dbfaa9c40b5cfdcb71a1d4e8bca0d45ca1055\"\n    },\n    \"com.github-glitchenzo.nugetforunity\": {\n      \"version\": \"https://github.com/GlitchEnzo/NuGetForUnity.git?path=/src/NuGetForUnity\",\n      \"depth\": 0,\n      \"source\": \"git\",\n      \"dependencies\": {},\n      \"hash\": \"7c80e98d3b56ecbcf854a9336458b6b0dedf1b8f\"\n    },\n    \"com.github.mastermemory.internal\": {\n      \"version\": \"file:C:/Users/S04451/Documents/GitHub/MasterMemory/src/MasterMemory/bin/Debug/netstandard2.0\",\n      \"depth\": 0,\n      \"source\": \"local\",\n      \"dependencies\": {}\n    },\n    \"com.unity.ext.nunit\": {\n      \"version\": \"1.0.6\",\n      \"depth\": 1,\n      \"source\": \"registry\",\n      \"dependencies\": {},\n      \"url\": \"https://packages.unity.com\"\n    },\n    \"com.unity.ide.rider\": {\n      \"version\": \"3.0.31\",\n      \"depth\": 0,\n      \"source\": \"registry\",\n      \"dependencies\": {\n        \"com.unity.ext.nunit\": \"1.0.6\"\n      },\n      \"url\": \"https://packages.unity.com\"\n    },\n    \"com.unity.ide.visualstudio\": {\n      \"version\": \"2.0.22\",\n      \"depth\": 0,\n      \"source\": \"registry\",\n      \"dependencies\": {\n        \"com.unity.test-framework\": \"1.1.9\"\n      },\n      \"url\": \"https://packages.unity.com\"\n    },\n    \"com.unity.ide.vscode\": {\n      \"version\": \"1.2.5\",\n      \"depth\": 0,\n      \"source\": \"registry\",\n      \"dependencies\": {},\n      \"url\": \"https://packages.unity.com\"\n    },\n    \"com.unity.sysroot\": {\n      \"version\": \"2.0.10\",\n      \"depth\": 1,\n      \"source\": \"registry\",\n      \"dependencies\": {},\n      \"url\": \"https://packages.unity.com\"\n    },\n    \"com.unity.sysroot.linux-x86_64\": {\n      \"version\": \"2.0.9\",\n      \"depth\": 1,\n      \"source\": \"registry\",\n      \"dependencies\": {\n        \"com.unity.sysroot\": \"2.0.10\"\n      },\n      \"url\": \"https://packages.unity.com\"\n    },\n    \"com.unity.test-framework\": {\n      \"version\": \"1.1.33\",\n      \"depth\": 0,\n      \"source\": \"registry\",\n      \"dependencies\": {\n        \"com.unity.ext.nunit\": \"1.0.6\",\n        \"com.unity.modules.imgui\": \"1.0.0\",\n        \"com.unity.modules.jsonserialize\": \"1.0.0\"\n      },\n      \"url\": \"https://packages.unity.com\"\n    },\n    \"com.unity.toolchain.win-x86_64-linux-x86_64\": {\n      \"version\": \"2.0.9\",\n      \"depth\": 0,\n      \"source\": \"registry\",\n      \"dependencies\": {\n        \"com.unity.sysroot\": \"2.0.10\",\n        \"com.unity.sysroot.linux-x86_64\": \"2.0.9\"\n      },\n      \"url\": \"https://packages.unity.com\"\n    },\n    \"com.unity.ugui\": {\n      \"version\": \"1.0.0\",\n      \"depth\": 0,\n      \"source\": \"builtin\",\n      \"dependencies\": {\n        \"com.unity.modules.ui\": \"1.0.0\",\n        \"com.unity.modules.imgui\": \"1.0.0\"\n      }\n    },\n    \"com.unity.modules.imgui\": {\n      \"version\": \"1.0.0\",\n      \"depth\": 1,\n      \"source\": \"builtin\",\n      \"dependencies\": {}\n    },\n    \"com.unity.modules.jsonserialize\": {\n      \"version\": \"1.0.0\",\n      \"depth\": 1,\n      \"source\": \"builtin\",\n      \"dependencies\": {}\n    },\n    \"com.unity.modules.ui\": {\n      \"version\": \"1.0.0\",\n      \"depth\": 1,\n      \"source\": \"builtin\",\n      \"dependencies\": {}\n    }\n  }\n}\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/AudioManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!11 &1\nAudioManager:\n  m_ObjectHideFlags: 0\n  m_Volume: 1\n  Rolloff Scale: 1\n  Doppler Factor: 1\n  Default Speaker Mode: 2\n  m_SampleRate: 0\n  m_DSPBufferSize: 1024\n  m_VirtualVoiceCount: 512\n  m_RealVoiceCount: 32\n  m_SpatializerPlugin: \n  m_AmbisonicDecoderPlugin: \n  m_DisableAudio: 0\n  m_VirtualizeEffects: 1\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/ClusterInputManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!236 &1\nClusterInputManager:\n  m_ObjectHideFlags: 0\n  m_Inputs: []\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/DynamicsManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!55 &1\nPhysicsManager:\n  m_ObjectHideFlags: 0\n  serializedVersion: 7\n  m_Gravity: {x: 0, y: -9.81, z: 0}\n  m_DefaultMaterial: {fileID: 0}\n  m_BounceThreshold: 2\n  m_SleepThreshold: 0.005\n  m_DefaultContactOffset: 0.01\n  m_DefaultSolverIterations: 6\n  m_DefaultSolverVelocityIterations: 1\n  m_QueriesHitBackfaces: 0\n  m_QueriesHitTriggers: 1\n  m_EnableAdaptiveForce: 0\n  m_ClothInterCollisionDistance: 0\n  m_ClothInterCollisionStiffness: 0\n  m_ContactsGeneration: 1\n  m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n  m_AutoSimulation: 1\n  m_AutoSyncTransforms: 0\n  m_ReuseCollisionCallbacks: 1\n  m_ClothInterCollisionSettingsToggle: 0\n  m_ContactPairsMode: 0\n  m_BroadphaseType: 0\n  m_WorldBounds:\n    m_Center: {x: 0, y: 0, z: 0}\n    m_Extent: {x: 250, y: 250, z: 250}\n  m_WorldSubdivisions: 8\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/EditorBuildSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!1045 &1\nEditorBuildSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  m_Scenes:\n  - enabled: 1\n    path: Assets/Scenes/SampleScene.unity\n    guid: 2cda990e2423bbf4892e6590ba056729\n  m_configObjects: {}\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/EditorSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!159 &1\nEditorSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 7\n  m_ExternalVersionControlSupport: Visible Meta Files\n  m_SerializationMode: 2\n  m_LineEndingsForNewScripts: 2\n  m_DefaultBehaviorMode: 1\n  m_SpritePackerMode: 4\n  m_SpritePackerPaddingPower: 1\n  m_EtcTextureCompressorBehavior: 1\n  m_EtcTextureFastCompressor: 1\n  m_EtcTextureNormalCompressor: 2\n  m_EtcTextureBestCompressor: 4\n  m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd\n  m_ProjectGenerationRootNamespace: \n  m_UserGeneratedProjectSuffix: \n  m_CollabEditorSettings:\n    inProgressEnabled: 1\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/GraphicsSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!30 &1\nGraphicsSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 12\n  m_Deferred:\n    m_Mode: 1\n    m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0}\n  m_DeferredReflections:\n    m_Mode: 1\n    m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0}\n  m_ScreenSpaceShadows:\n    m_Mode: 1\n    m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0}\n  m_LegacyDeferred:\n    m_Mode: 1\n    m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0}\n  m_DepthNormals:\n    m_Mode: 1\n    m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0}\n  m_MotionVectors:\n    m_Mode: 1\n    m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0}\n  m_LightHalo:\n    m_Mode: 1\n    m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0}\n  m_LensFlare:\n    m_Mode: 1\n    m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0}\n  m_AlwaysIncludedShaders:\n  - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0}\n  - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0}\n  - {fileID: 16000, guid: 0000000000000000f000000000000000, type: 0}\n  - {fileID: 16001, guid: 0000000000000000f000000000000000, type: 0}\n  - {fileID: 17000, guid: 0000000000000000f000000000000000, type: 0}\n  m_PreloadedShaders: []\n  m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000,\n    type: 0}\n  m_CustomRenderPipeline: {fileID: 0}\n  m_TransparencySortMode: 0\n  m_TransparencySortAxis: {x: 0, y: 0, z: 1}\n  m_DefaultRenderingPath: 1\n  m_DefaultMobileRenderingPath: 1\n  m_TierSettings: []\n  m_LightmapStripping: 0\n  m_FogStripping: 0\n  m_InstancingStripping: 0\n  m_LightmapKeepPlain: 1\n  m_LightmapKeepDirCombined: 1\n  m_LightmapKeepDynamicPlain: 1\n  m_LightmapKeepDynamicDirCombined: 1\n  m_LightmapKeepShadowMask: 1\n  m_LightmapKeepSubtractive: 1\n  m_FogKeepLinear: 1\n  m_FogKeepExp: 1\n  m_FogKeepExp2: 1\n  m_AlbedoSwatchInfos: []\n  m_LightsUseLinearIntensity: 0\n  m_LightsUseColorTemperature: 0\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/InputManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!13 &1\nInputManager:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  m_Axes:\n  - serializedVersion: 3\n    m_Name: Horizontal\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: left\n    positiveButton: right\n    altNegativeButton: a\n    altPositiveButton: d\n    gravity: 3\n    dead: 0.001\n    sensitivity: 3\n    snap: 1\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Vertical\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: down\n    positiveButton: up\n    altNegativeButton: s\n    altPositiveButton: w\n    gravity: 3\n    dead: 0.001\n    sensitivity: 3\n    snap: 1\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire1\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: left ctrl\n    altNegativeButton: \n    altPositiveButton: mouse 0\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire2\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: left alt\n    altNegativeButton: \n    altPositiveButton: mouse 1\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire3\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: left shift\n    altNegativeButton: \n    altPositiveButton: mouse 2\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Jump\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: space\n    altNegativeButton: \n    altPositiveButton: \n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Mouse X\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: \n    altNegativeButton: \n    altPositiveButton: \n    gravity: 0\n    dead: 0\n    sensitivity: 0.1\n    snap: 0\n    invert: 0\n    type: 1\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Mouse Y\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: \n    altNegativeButton: \n    altPositiveButton: \n    gravity: 0\n    dead: 0\n    sensitivity: 0.1\n    snap: 0\n    invert: 0\n    type: 1\n    axis: 1\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Mouse ScrollWheel\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: \n    altNegativeButton: \n    altPositiveButton: \n    gravity: 0\n    dead: 0\n    sensitivity: 0.1\n    snap: 0\n    invert: 0\n    type: 1\n    axis: 2\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Horizontal\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: \n    altNegativeButton: \n    altPositiveButton: \n    gravity: 0\n    dead: 0.19\n    sensitivity: 1\n    snap: 0\n    invert: 0\n    type: 2\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Vertical\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: \n    altNegativeButton: \n    altPositiveButton: \n    gravity: 0\n    dead: 0.19\n    sensitivity: 1\n    snap: 0\n    invert: 1\n    type: 2\n    axis: 1\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire1\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: joystick button 0\n    altNegativeButton: \n    altPositiveButton: \n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire2\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: joystick button 1\n    altNegativeButton: \n    altPositiveButton: \n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire3\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: joystick button 2\n    altNegativeButton: \n    altPositiveButton: \n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Jump\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: joystick button 3\n    altNegativeButton: \n    altPositiveButton: \n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Submit\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: return\n    altNegativeButton: \n    altPositiveButton: joystick button 0\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Submit\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: enter\n    altNegativeButton: \n    altPositiveButton: space\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Cancel\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: escape\n    altNegativeButton: \n    altPositiveButton: joystick button 1\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/MemorySettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!387306366 &1\nMemorySettings:\n  m_ObjectHideFlags: 0\n  m_EditorMemorySettings:\n    m_MainAllocatorBlockSize: -1\n    m_ThreadAllocatorBlockSize: -1\n    m_MainGfxBlockSize: -1\n    m_ThreadGfxBlockSize: -1\n    m_CacheBlockSize: -1\n    m_TypetreeBlockSize: -1\n    m_ProfilerBlockSize: -1\n    m_ProfilerEditorBlockSize: -1\n    m_BucketAllocatorGranularity: -1\n    m_BucketAllocatorBucketsCount: -1\n    m_BucketAllocatorBlockSize: -1\n    m_BucketAllocatorBlockCount: -1\n    m_ProfilerBucketAllocatorGranularity: -1\n    m_ProfilerBucketAllocatorBucketsCount: -1\n    m_ProfilerBucketAllocatorBlockSize: -1\n    m_ProfilerBucketAllocatorBlockCount: -1\n    m_TempAllocatorSizeMain: -1\n    m_JobTempAllocatorBlockSize: -1\n    m_BackgroundJobTempAllocatorBlockSize: -1\n    m_JobTempAllocatorReducedBlockSize: -1\n    m_TempAllocatorSizeGIBakingWorker: -1\n    m_TempAllocatorSizeNavMeshWorker: -1\n    m_TempAllocatorSizeAudioWorker: -1\n    m_TempAllocatorSizeCloudWorker: -1\n    m_TempAllocatorSizeGfx: -1\n    m_TempAllocatorSizeJobWorker: -1\n    m_TempAllocatorSizeBackgroundWorker: -1\n    m_TempAllocatorSizePreloadManager: -1\n  m_PlatformMemorySettings: {}\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/NavMeshAreas.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!126 &1\nNavMeshProjectSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  areas:\n  - name: Walkable\n    cost: 1\n  - name: Not Walkable\n    cost: 1\n  - name: Jump\n    cost: 2\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  m_LastAgentTypeID: -887442657\n  m_Settings:\n  - serializedVersion: 2\n    agentTypeID: 0\n    agentRadius: 0.5\n    agentHeight: 2\n    agentSlope: 45\n    agentClimb: 0.75\n    ledgeDropHeight: 0\n    maxJumpAcrossDistance: 0\n    minRegionArea: 2\n    manualCellSize: 0\n    cellSize: 0.16666667\n    manualTileSize: 0\n    tileSize: 256\n    accuratePlacement: 0\n    debug:\n      m_Flags: 0\n  m_SettingNames:\n  - Humanoid\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/NetworkManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!149 &1\nNetworkManager:\n  m_ObjectHideFlags: 0\n  m_DebugLevel: 0\n  m_Sendrate: 15\n  m_AssetToPrefab: {}\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/PackageManagerSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!114 &1\nMonoBehaviour:\n  m_ObjectHideFlags: 53\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 0}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_EnablePreReleasePackages: 0\n  m_AdvancedSettingsExpanded: 1\n  m_ScopedRegistriesSettingsExpanded: 1\n  m_SeeAllPackageVersions: 0\n  m_DismissPreviewPackagesInUse: 0\n  oneTimeWarningShown: 0\n  m_Registries:\n  - m_Id: main\n    m_Name: \n    m_Url: https://packages.unity.com\n    m_Scopes: []\n    m_IsDefault: 1\n    m_Capabilities: 7\n    m_ConfigSource: 0\n  m_UserSelectedRegistryName: \n  m_UserAddingNewScopedRegistry: 0\n  m_RegistryInfoDraft:\n    m_Modified: 0\n    m_ErrorMessage: \n    m_UserModificationsInstanceId: -856\n    m_OriginalInstanceId: -858\n  m_LoadAssets: 0\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/Physics2DSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!19 &1\nPhysics2DSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 4\n  m_Gravity: {x: 0, y: -9.81}\n  m_DefaultMaterial: {fileID: 0}\n  m_VelocityIterations: 8\n  m_PositionIterations: 3\n  m_VelocityThreshold: 1\n  m_MaxLinearCorrection: 0.2\n  m_MaxAngularCorrection: 8\n  m_MaxTranslationSpeed: 100\n  m_MaxRotationSpeed: 360\n  m_BaumgarteScale: 0.2\n  m_BaumgarteTimeOfImpactScale: 0.75\n  m_TimeToSleep: 0.5\n  m_LinearSleepTolerance: 0.01\n  m_AngularSleepTolerance: 2\n  m_DefaultContactOffset: 0.01\n  m_JobOptions:\n    serializedVersion: 2\n    useMultithreading: 0\n    useConsistencySorting: 0\n    m_InterpolationPosesPerJob: 100\n    m_NewContactsPerJob: 30\n    m_CollideContactsPerJob: 100\n    m_ClearFlagsPerJob: 200\n    m_ClearBodyForcesPerJob: 200\n    m_SyncDiscreteFixturesPerJob: 50\n    m_SyncContinuousFixturesPerJob: 50\n    m_FindNearestContactsPerJob: 100\n    m_UpdateTriggerContactsPerJob: 100\n    m_IslandSolverCostThreshold: 100\n    m_IslandSolverBodyCostScale: 1\n    m_IslandSolverContactCostScale: 10\n    m_IslandSolverJointCostScale: 10\n    m_IslandSolverBodiesPerJob: 50\n    m_IslandSolverContactsPerJob: 50\n  m_AutoSimulation: 1\n  m_QueriesHitTriggers: 1\n  m_QueriesStartInColliders: 1\n  m_CallbacksOnDisable: 1\n  m_ReuseCollisionCallbacks: 0\n  m_AutoSyncTransforms: 0\n  m_AlwaysShowColliders: 0\n  m_ShowColliderSleep: 1\n  m_ShowColliderContacts: 0\n  m_ShowColliderAABB: 0\n  m_ContactArrowScale: 0.2\n  m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412}\n  m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432}\n  m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745}\n  m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804}\n  m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/PresetManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!1386491679 &1\nPresetManager:\n  m_ObjectHideFlags: 0\n  m_DefaultList: []\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/ProjectSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!129 &1\nPlayerSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 26\n  productGUID: ffc3e654a5b5f54478b59336189e576a\n  AndroidProfiler: 0\n  AndroidFilterTouchesWhenObscured: 0\n  AndroidEnableSustainedPerformanceMode: 0\n  defaultScreenOrientation: 4\n  targetDevice: 2\n  useOnDemandResources: 0\n  accelerometerFrequency: 60\n  companyName: DefaultCompany\n  productName: MasterMemory\n  defaultCursor: {fileID: 0}\n  cursorHotspot: {x: 0, y: 0}\n  m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1}\n  m_ShowUnitySplashScreen: 1\n  m_ShowUnitySplashLogo: 1\n  m_SplashScreenOverlayOpacity: 1\n  m_SplashScreenAnimation: 1\n  m_SplashScreenLogoStyle: 1\n  m_SplashScreenDrawMode: 0\n  m_SplashScreenBackgroundAnimationZoom: 1\n  m_SplashScreenLogoAnimationZoom: 1\n  m_SplashScreenBackgroundLandscapeAspect: 1\n  m_SplashScreenBackgroundPortraitAspect: 1\n  m_SplashScreenBackgroundLandscapeUvs:\n    serializedVersion: 2\n    x: 0\n    y: 0\n    width: 1\n    height: 1\n  m_SplashScreenBackgroundPortraitUvs:\n    serializedVersion: 2\n    x: 0\n    y: 0\n    width: 1\n    height: 1\n  m_SplashScreenLogos: []\n  m_VirtualRealitySplashScreen: {fileID: 0}\n  m_HolographicTrackingLossScreen: {fileID: 0}\n  defaultScreenWidth: 1024\n  defaultScreenHeight: 768\n  defaultScreenWidthWeb: 960\n  defaultScreenHeightWeb: 600\n  m_StereoRenderingPath: 0\n  m_ActiveColorSpace: 0\n  unsupportedMSAAFallback: 0\n  m_SpriteBatchVertexThreshold: 300\n  m_MTRendering: 1\n  mipStripping: 0\n  numberOfMipsStripped: 0\n  numberOfMipsStrippedPerMipmapLimitGroup: {}\n  m_StackTraceTypes: 010000000100000001000000010000000100000001000000\n  iosShowActivityIndicatorOnLoading: -1\n  androidShowActivityIndicatorOnLoading: -1\n  iosUseCustomAppBackgroundBehavior: 0\n  allowedAutorotateToPortrait: 1\n  allowedAutorotateToPortraitUpsideDown: 1\n  allowedAutorotateToLandscapeRight: 1\n  allowedAutorotateToLandscapeLeft: 1\n  useOSAutorotation: 1\n  use32BitDisplayBuffer: 1\n  preserveFramebufferAlpha: 0\n  disableDepthAndStencilBuffers: 0\n  androidStartInFullscreen: 1\n  androidRenderOutsideSafeArea: 1\n  androidUseSwappy: 0\n  androidBlitType: 0\n  androidResizableWindow: 0\n  androidDefaultWindowWidth: 1920\n  androidDefaultWindowHeight: 1080\n  androidMinimumWindowWidth: 400\n  androidMinimumWindowHeight: 300\n  androidFullscreenMode: 1\n  defaultIsNativeResolution: 1\n  macRetinaSupport: 1\n  runInBackground: 1\n  captureSingleScreen: 0\n  muteOtherAudioSources: 0\n  Prepare IOS For Recording: 0\n  Force IOS Speakers When Recording: 0\n  deferSystemGesturesMode: 0\n  hideHomeButton: 0\n  submitAnalytics: 1\n  usePlayerLog: 1\n  dedicatedServerOptimizations: 0\n  bakeCollisionMeshes: 0\n  forceSingleInstance: 0\n  useFlipModelSwapchain: 1\n  resizableWindow: 0\n  useMacAppStoreValidation: 0\n  macAppStoreCategory: public.app-category.games\n  gpuSkinning: 0\n  xboxPIXTextureCapture: 0\n  xboxEnableAvatar: 0\n  xboxEnableKinect: 0\n  xboxEnableKinectAutoTracking: 0\n  xboxEnableFitness: 0\n  visibleInBackground: 1\n  allowFullscreenSwitch: 1\n  fullscreenMode: 1\n  xboxSpeechDB: 0\n  xboxEnableHeadOrientation: 0\n  xboxEnableGuest: 0\n  xboxEnablePIXSampling: 0\n  metalFramebufferOnly: 0\n  xboxOneResolution: 0\n  xboxOneSResolution: 0\n  xboxOneXResolution: 3\n  xboxOneMonoLoggingLevel: 0\n  xboxOneLoggingLevel: 1\n  xboxOneDisableEsram: 0\n  xboxOneEnableTypeOptimization: 0\n  xboxOnePresentImmediateThreshold: 0\n  switchQueueCommandMemory: 0\n  switchQueueControlMemory: 16384\n  switchQueueComputeMemory: 262144\n  switchNVNShaderPoolsGranularity: 33554432\n  switchNVNDefaultPoolsGranularity: 16777216\n  switchNVNOtherPoolsGranularity: 16777216\n  switchGpuScratchPoolGranularity: 2097152\n  switchAllowGpuScratchShrinking: 0\n  switchNVNMaxPublicTextureIDCount: 0\n  switchNVNMaxPublicSamplerIDCount: 0\n  switchNVNGraphicsFirmwareMemory: 32\n  switchMaxWorkerMultiple: 8\n  stadiaPresentMode: 0\n  stadiaTargetFramerate: 0\n  vulkanNumSwapchainBuffers: 3\n  vulkanEnableSetSRGBWrite: 0\n  vulkanEnablePreTransform: 0\n  vulkanEnableLateAcquireNextImage: 0\n  vulkanEnableCommandBufferRecycling: 1\n  loadStoreDebugModeEnabled: 0\n  bundleVersion: 0.1\n  preloadedAssets: []\n  metroInputSource: 0\n  wsaTransparentSwapchain: 0\n  m_HolographicPauseOnTrackingLoss: 1\n  xboxOneDisableKinectGpuReservation: 1\n  xboxOneEnable7thCore: 1\n  vrSettings:\n    enable360StereoCapture: 0\n  isWsaHolographicRemotingEnabled: 0\n  enableFrameTimingStats: 0\n  enableOpenGLProfilerGPURecorders: 1\n  allowHDRDisplaySupport: 0\n  useHDRDisplay: 0\n  hdrBitDepth: 0\n  m_ColorGamuts: 00000000\n  targetPixelDensity: 30\n  resolutionScalingMode: 0\n  resetResolutionOnWindowResize: 0\n  androidSupportedAspectRatio: 1\n  androidMaxAspectRatio: 2.1\n  applicationIdentifier:\n    Standalone: com.DefaultCompany.MasterMemory\n  buildNumber:\n    Standalone: 0\n    VisionOS: 0\n    iPhone: 0\n    tvOS: 0\n  overrideDefaultApplicationIdentifier: 0\n  AndroidBundleVersionCode: 1\n  AndroidMinSdkVersion: 22\n  AndroidTargetSdkVersion: 0\n  AndroidPreferredInstallLocation: 1\n  aotOptions: \n  stripEngineCode: 1\n  iPhoneStrippingLevel: 0\n  iPhoneScriptCallOptimization: 0\n  ForceInternetPermission: 0\n  ForceSDCardPermission: 0\n  CreateWallpaper: 0\n  APKExpansionFiles: 0\n  keepLoadedShadersAlive: 0\n  StripUnusedMeshComponents: 1\n  strictShaderVariantMatching: 0\n  VertexChannelCompressionMask: 4054\n  iPhoneSdkVersion: 988\n  iOSTargetOSVersionString: 12.0\n  tvOSSdkVersion: 0\n  tvOSRequireExtendedGameController: 0\n  tvOSTargetOSVersionString: 12.0\n  VisionOSSdkVersion: 0\n  VisionOSTargetOSVersionString: 1.0\n  uIPrerenderedIcon: 0\n  uIRequiresPersistentWiFi: 0\n  uIRequiresFullScreen: 1\n  uIStatusBarHidden: 1\n  uIExitOnSuspend: 0\n  uIStatusBarStyle: 0\n  appleTVSplashScreen: {fileID: 0}\n  appleTVSplashScreen2x: {fileID: 0}\n  tvOSSmallIconLayers: []\n  tvOSSmallIconLayers2x: []\n  tvOSLargeIconLayers: []\n  tvOSLargeIconLayers2x: []\n  tvOSTopShelfImageLayers: []\n  tvOSTopShelfImageLayers2x: []\n  tvOSTopShelfImageWideLayers: []\n  tvOSTopShelfImageWideLayers2x: []\n  iOSLaunchScreenType: 0\n  iOSLaunchScreenPortrait: {fileID: 0}\n  iOSLaunchScreenLandscape: {fileID: 0}\n  iOSLaunchScreenBackgroundColor:\n    serializedVersion: 2\n    rgba: 0\n  iOSLaunchScreenFillPct: 100\n  iOSLaunchScreenSize: 100\n  iOSLaunchScreenCustomXibPath: \n  iOSLaunchScreeniPadType: 0\n  iOSLaunchScreeniPadImage: {fileID: 0}\n  iOSLaunchScreeniPadBackgroundColor:\n    serializedVersion: 2\n    rgba: 0\n  iOSLaunchScreeniPadFillPct: 100\n  iOSLaunchScreeniPadSize: 100\n  iOSLaunchScreeniPadCustomXibPath: \n  iOSLaunchScreenCustomStoryboardPath: \n  iOSLaunchScreeniPadCustomStoryboardPath: \n  iOSDeviceRequirements: []\n  iOSURLSchemes: []\n  macOSURLSchemes: []\n  iOSBackgroundModes: 0\n  iOSMetalForceHardShadows: 0\n  metalEditorSupport: 1\n  metalAPIValidation: 1\n  iOSRenderExtraFrameOnPause: 0\n  iosCopyPluginsCodeInsteadOfSymlink: 0\n  appleDeveloperTeamID: \n  iOSManualSigningProvisioningProfileID: \n  tvOSManualSigningProvisioningProfileID: \n  VisionOSManualSigningProvisioningProfileID: \n  iOSManualSigningProvisioningProfileType: 0\n  tvOSManualSigningProvisioningProfileType: 0\n  VisionOSManualSigningProvisioningProfileType: 0\n  appleEnableAutomaticSigning: 0\n  iOSRequireARKit: 0\n  iOSAutomaticallyDetectAndAddCapabilities: 1\n  appleEnableProMotion: 0\n  shaderPrecisionModel: 0\n  clonedFromGUID: 5f34be1353de5cf4398729fda238591b\n  templatePackageId: com.unity.template.2d@3.1.0\n  templateDefaultScene: Assets/Scenes/SampleScene.unity\n  useCustomMainManifest: 0\n  useCustomLauncherManifest: 0\n  useCustomMainGradleTemplate: 0\n  useCustomLauncherGradleManifest: 0\n  useCustomBaseGradleTemplate: 0\n  useCustomGradlePropertiesTemplate: 0\n  useCustomGradleSettingsTemplate: 0\n  useCustomProguardFile: 0\n  AndroidTargetArchitectures: 1\n  AndroidTargetDevices: 0\n  AndroidSplashScreenScale: 0\n  androidSplashScreen: {fileID: 0}\n  AndroidKeystoreName: '{inproject}: '\n  AndroidKeyaliasName: \n  AndroidEnableArmv9SecurityFeatures: 0\n  AndroidBuildApkPerCpuArchitecture: 0\n  AndroidTVCompatibility: 0\n  AndroidIsGame: 1\n  AndroidEnableTango: 0\n  androidEnableBanner: 1\n  androidUseLowAccuracyLocation: 0\n  androidUseCustomKeystore: 0\n  m_AndroidBanners:\n  - width: 320\n    height: 180\n    banner: {fileID: 0}\n  androidGamepadSupportLevel: 0\n  chromeosInputEmulation: 1\n  AndroidMinifyRelease: 0\n  AndroidMinifyDebug: 0\n  AndroidValidateAppBundleSize: 1\n  AndroidAppBundleSizeToValidate: 100\n  m_BuildTargetIcons: []\n  m_BuildTargetPlatformIcons: []\n  m_BuildTargetBatching: []\n  m_BuildTargetShaderSettings: []\n  m_BuildTargetGraphicsJobs:\n  - m_BuildTarget: WindowsStandaloneSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: MacStandaloneSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: LinuxStandaloneSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: AndroidPlayer\n    m_GraphicsJobs: 0\n  - m_BuildTarget: iOSSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: PS4Player\n    m_GraphicsJobs: 0\n  - m_BuildTarget: PS5Player\n    m_GraphicsJobs: 0\n  - m_BuildTarget: XboxOnePlayer\n    m_GraphicsJobs: 0\n  - m_BuildTarget: GameCoreXboxOneSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: GameCoreScarlettSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: Switch\n    m_GraphicsJobs: 0\n  - m_BuildTarget: WebGLSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: MetroSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: AppleTVSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: VisionOSPlayer\n    m_GraphicsJobs: 0\n  - m_BuildTarget: BJMSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: CloudRendering\n    m_GraphicsJobs: 0\n  - m_BuildTarget: EmbeddedLinux\n    m_GraphicsJobs: 0\n  - m_BuildTarget: QNX\n    m_GraphicsJobs: 0\n  m_BuildTargetGraphicsJobMode:\n  - m_BuildTarget: PS4Player\n    m_GraphicsJobMode: 0\n  - m_BuildTarget: XboxOnePlayer\n    m_GraphicsJobMode: 0\n  m_BuildTargetGraphicsAPIs:\n  - m_BuildTarget: AndroidPlayer\n    m_APIs: 150000000b000000\n    m_Automatic: 1\n  - m_BuildTarget: iOSSupport\n    m_APIs: 10000000\n    m_Automatic: 1\n  m_BuildTargetVRSettings: []\n  m_DefaultShaderChunkSizeInMB: 16\n  m_DefaultShaderChunkCount: 0\n  openGLRequireES31: 0\n  openGLRequireES31AEP: 0\n  openGLRequireES32: 0\n  m_TemplateCustomTags: {}\n  mobileMTRendering:\n    Android: 1\n    iPhone: 1\n    tvOS: 1\n  m_BuildTargetGroupLightmapEncodingQuality: []\n  m_BuildTargetGroupHDRCubemapEncodingQuality: []\n  m_BuildTargetGroupLightmapSettings: []\n  m_BuildTargetGroupLoadStoreDebugModeSettings: []\n  m_BuildTargetNormalMapEncoding: []\n  m_BuildTargetDefaultTextureCompressionFormat: []\n  playModeTestRunnerEnabled: 1\n  runPlayModeTestAsEditModeTest: 0\n  actionOnDotNetUnhandledException: 1\n  enableInternalProfiler: 0\n  logObjCUncaughtExceptions: 1\n  enableCrashReportAPI: 0\n  cameraUsageDescription: \n  locationUsageDescription: \n  microphoneUsageDescription: \n  bluetoothUsageDescription: \n  macOSTargetOSVersion: 10.13.0\n  switchNMETAOverride: \n  switchNetLibKey: \n  switchSocketMemoryPoolSize: 6144\n  switchSocketAllocatorPoolSize: 128\n  switchSocketConcurrencyLimit: 14\n  switchScreenResolutionBehavior: 2\n  switchUseCPUProfiler: 0\n  switchEnableFileSystemTrace: 0\n  switchUseGOLDLinker: 0\n  switchLTOSetting: 0\n  switchApplicationID: 0x01004b9000490000\n  switchNSODependencies: \n  switchCompilerFlags: \n  switchTitleNames_0: \n  switchTitleNames_1: \n  switchTitleNames_2: \n  switchTitleNames_3: \n  switchTitleNames_4: \n  switchTitleNames_5: \n  switchTitleNames_6: \n  switchTitleNames_7: \n  switchTitleNames_8: \n  switchTitleNames_9: \n  switchTitleNames_10: \n  switchTitleNames_11: \n  switchTitleNames_12: \n  switchTitleNames_13: \n  switchTitleNames_14: \n  switchTitleNames_15: \n  switchPublisherNames_0: \n  switchPublisherNames_1: \n  switchPublisherNames_2: \n  switchPublisherNames_3: \n  switchPublisherNames_4: \n  switchPublisherNames_5: \n  switchPublisherNames_6: \n  switchPublisherNames_7: \n  switchPublisherNames_8: \n  switchPublisherNames_9: \n  switchPublisherNames_10: \n  switchPublisherNames_11: \n  switchPublisherNames_12: \n  switchPublisherNames_13: \n  switchPublisherNames_14: \n  switchPublisherNames_15: \n  switchIcons_0: {fileID: 0}\n  switchIcons_1: {fileID: 0}\n  switchIcons_2: {fileID: 0}\n  switchIcons_3: {fileID: 0}\n  switchIcons_4: {fileID: 0}\n  switchIcons_5: {fileID: 0}\n  switchIcons_6: {fileID: 0}\n  switchIcons_7: {fileID: 0}\n  switchIcons_8: {fileID: 0}\n  switchIcons_9: {fileID: 0}\n  switchIcons_10: {fileID: 0}\n  switchIcons_11: {fileID: 0}\n  switchIcons_12: {fileID: 0}\n  switchIcons_13: {fileID: 0}\n  switchIcons_14: {fileID: 0}\n  switchIcons_15: {fileID: 0}\n  switchSmallIcons_0: {fileID: 0}\n  switchSmallIcons_1: {fileID: 0}\n  switchSmallIcons_2: {fileID: 0}\n  switchSmallIcons_3: {fileID: 0}\n  switchSmallIcons_4: {fileID: 0}\n  switchSmallIcons_5: {fileID: 0}\n  switchSmallIcons_6: {fileID: 0}\n  switchSmallIcons_7: {fileID: 0}\n  switchSmallIcons_8: {fileID: 0}\n  switchSmallIcons_9: {fileID: 0}\n  switchSmallIcons_10: {fileID: 0}\n  switchSmallIcons_11: {fileID: 0}\n  switchSmallIcons_12: {fileID: 0}\n  switchSmallIcons_13: {fileID: 0}\n  switchSmallIcons_14: {fileID: 0}\n  switchSmallIcons_15: {fileID: 0}\n  switchManualHTML: \n  switchAccessibleURLs: \n  switchLegalInformation: \n  switchMainThreadStackSize: 1048576\n  switchPresenceGroupId: \n  switchLogoHandling: 0\n  switchReleaseVersion: 0\n  switchDisplayVersion: 1.0.0\n  switchStartupUserAccount: 0\n  switchSupportedLanguagesMask: 0\n  switchLogoType: 0\n  switchApplicationErrorCodeCategory: \n  switchUserAccountSaveDataSize: 0\n  switchUserAccountSaveDataJournalSize: 0\n  switchApplicationAttribute: 0\n  switchCardSpecSize: -1\n  switchCardSpecClock: -1\n  switchRatingsMask: 0\n  switchRatingsInt_0: 0\n  switchRatingsInt_1: 0\n  switchRatingsInt_2: 0\n  switchRatingsInt_3: 0\n  switchRatingsInt_4: 0\n  switchRatingsInt_5: 0\n  switchRatingsInt_6: 0\n  switchRatingsInt_7: 0\n  switchRatingsInt_8: 0\n  switchRatingsInt_9: 0\n  switchRatingsInt_10: 0\n  switchRatingsInt_11: 0\n  switchRatingsInt_12: 0\n  switchLocalCommunicationIds_0: \n  switchLocalCommunicationIds_1: \n  switchLocalCommunicationIds_2: \n  switchLocalCommunicationIds_3: \n  switchLocalCommunicationIds_4: \n  switchLocalCommunicationIds_5: \n  switchLocalCommunicationIds_6: \n  switchLocalCommunicationIds_7: \n  switchParentalControl: 0\n  switchAllowsScreenshot: 1\n  switchAllowsVideoCapturing: 1\n  switchAllowsRuntimeAddOnContentInstall: 0\n  switchDataLossConfirmation: 0\n  switchUserAccountLockEnabled: 0\n  switchSystemResourceMemory: 16777216\n  switchSupportedNpadStyles: 3\n  switchNativeFsCacheSize: 32\n  switchIsHoldTypeHorizontal: 0\n  switchSupportedNpadCount: 8\n  switchEnableTouchScreen: 1\n  switchSocketConfigEnabled: 0\n  switchTcpInitialSendBufferSize: 32\n  switchTcpInitialReceiveBufferSize: 64\n  switchTcpAutoSendBufferSizeMax: 256\n  switchTcpAutoReceiveBufferSizeMax: 256\n  switchUdpSendBufferSize: 9\n  switchUdpReceiveBufferSize: 42\n  switchSocketBufferEfficiency: 4\n  switchSocketInitializeEnabled: 1\n  switchNetworkInterfaceManagerInitializeEnabled: 1\n  switchUseNewStyleFilepaths: 1\n  switchUseLegacyFmodPriorities: 0\n  switchUseMicroSleepForYield: 1\n  switchEnableRamDiskSupport: 0\n  switchMicroSleepForYieldTime: 25\n  switchRamDiskSpaceSize: 12\n  ps4NPAgeRating: 12\n  ps4NPTitleSecret: \n  ps4NPTrophyPackPath: \n  ps4ParentalLevel: 11\n  ps4ContentID: ED1633-NPXX51362_00-0000000000000000\n  ps4Category: 0\n  ps4MasterVersion: 01.00\n  ps4AppVersion: 01.00\n  ps4AppType: 0\n  ps4ParamSfxPath: \n  ps4VideoOutPixelFormat: 0\n  ps4VideoOutInitialWidth: 1920\n  ps4VideoOutBaseModeInitialWidth: 1920\n  ps4VideoOutReprojectionRate: 60\n  ps4PronunciationXMLPath: \n  ps4PronunciationSIGPath: \n  ps4BackgroundImagePath: \n  ps4StartupImagePath: \n  ps4StartupImagesFolder: \n  ps4IconImagesFolder: \n  ps4SaveDataImagePath: \n  ps4SdkOverride: \n  ps4BGMPath: \n  ps4ShareFilePath: \n  ps4ShareOverlayImagePath: \n  ps4PrivacyGuardImagePath: \n  ps4ExtraSceSysFile: \n  ps4NPtitleDatPath: \n  ps4RemotePlayKeyAssignment: -1\n  ps4RemotePlayKeyMappingDir: \n  ps4PlayTogetherPlayerCount: 0\n  ps4EnterButtonAssignment: 1\n  ps4ApplicationParam1: 0\n  ps4ApplicationParam2: 0\n  ps4ApplicationParam3: 0\n  ps4ApplicationParam4: 0\n  ps4DownloadDataSize: 0\n  ps4GarlicHeapSize: 2048\n  ps4ProGarlicHeapSize: 2560\n  playerPrefsMaxSize: 32768\n  ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ\n  ps4pnSessions: 1\n  ps4pnPresence: 1\n  ps4pnFriends: 1\n  ps4pnGameCustomData: 1\n  playerPrefsSupport: 0\n  enableApplicationExit: 0\n  resetTempFolder: 1\n  restrictedAudioUsageRights: 0\n  ps4UseResolutionFallback: 0\n  ps4ReprojectionSupport: 0\n  ps4UseAudio3dBackend: 0\n  ps4UseLowGarlicFragmentationMode: 1\n  ps4SocialScreenEnabled: 0\n  ps4ScriptOptimizationLevel: 0\n  ps4Audio3dVirtualSpeakerCount: 14\n  ps4attribCpuUsage: 0\n  ps4PatchPkgPath: \n  ps4PatchLatestPkgPath: \n  ps4PatchChangeinfoPath: \n  ps4PatchDayOne: 0\n  ps4attribUserManagement: 0\n  ps4attribMoveSupport: 0\n  ps4attrib3DSupport: 0\n  ps4attribShareSupport: 0\n  ps4attribExclusiveVR: 0\n  ps4disableAutoHideSplash: 0\n  ps4videoRecordingFeaturesUsed: 0\n  ps4contentSearchFeaturesUsed: 0\n  ps4CompatibilityPS5: 0\n  ps4AllowPS5Detection: 0\n  ps4GPU800MHz: 1\n  ps4attribEyeToEyeDistanceSettingVR: 0\n  ps4IncludedModules: []\n  ps4attribVROutputEnabled: 0\n  monoEnv: \n  splashScreenBackgroundSourceLandscape: {fileID: 0}\n  splashScreenBackgroundSourcePortrait: {fileID: 0}\n  blurSplashScreenBackground: 1\n  spritePackerPolicy: \n  webGLMemorySize: 16\n  webGLExceptionSupport: 1\n  webGLNameFilesAsHashes: 0\n  webGLShowDiagnostics: 0\n  webGLDataCaching: 1\n  webGLDebugSymbols: 0\n  webGLEmscriptenArgs: \n  webGLModulesDirectory: \n  webGLTemplate: APPLICATION:Default\n  webGLAnalyzeBuildSize: 0\n  webGLUseEmbeddedResources: 0\n  webGLCompressionFormat: 1\n  webGLWasmArithmeticExceptions: 0\n  webGLLinkerTarget: 1\n  webGLThreadsSupport: 0\n  webGLDecompressionFallback: 0\n  webGLInitialMemorySize: 32\n  webGLMaximumMemorySize: 2048\n  webGLMemoryGrowthMode: 2\n  webGLMemoryLinearGrowthStep: 16\n  webGLMemoryGeometricGrowthStep: 0.2\n  webGLMemoryGeometricGrowthCap: 96\n  webGLPowerPreference: 2\n  scriptingDefineSymbols: {}\n  additionalCompilerArguments: {}\n  platformArchitecture: {}\n  scriptingBackend:\n    Standalone: 0\n  il2cppCompilerConfiguration: {}\n  il2cppCodeGeneration: {}\n  managedStrippingLevel:\n    EmbeddedLinux: 1\n    GameCoreScarlett: 1\n    GameCoreXboxOne: 1\n    Nintendo Switch: 1\n    PS4: 1\n    PS5: 1\n    QNX: 1\n    Stadia: 1\n    VisionOS: 1\n    WebGL: 1\n    Windows Store Apps: 1\n    XboxOne: 1\n    iPhone: 1\n    tvOS: 1\n  incrementalIl2cppBuild: {}\n  suppressCommonWarnings: 1\n  allowUnsafeCode: 0\n  useDeterministicCompilation: 1\n  additionalIl2CppArgs: \n  scriptingRuntimeVersion: 1\n  gcIncremental: 0\n  gcWBarrierValidation: 0\n  apiCompatibilityLevelPerPlatform: {}\n  m_RenderingPath: 1\n  m_MobileRenderingPath: 1\n  metroPackageName: Template_2D\n  metroPackageVersion: \n  metroCertificatePath: \n  metroCertificatePassword: \n  metroCertificateSubject: \n  metroCertificateIssuer: \n  metroCertificateNotAfter: 0000000000000000\n  metroApplicationDescription: Template_2D\n  wsaImages: {}\n  metroTileShortName: \n  metroTileShowName: 0\n  metroMediumTileShowName: 0\n  metroLargeTileShowName: 0\n  metroWideTileShowName: 0\n  metroSupportStreamingInstall: 0\n  metroLastRequiredScene: 0\n  metroDefaultTileSize: 1\n  metroTileForegroundText: 2\n  metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0}\n  metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628,\n    a: 1}\n  metroSplashScreenUseBackgroundColor: 0\n  platformCapabilities: {}\n  metroTargetDeviceFamilies: {}\n  metroFTAName: \n  metroFTAFileTypes: []\n  metroProtocolName: \n  vcxProjDefaultLanguage: \n  XboxOneProductId: \n  XboxOneUpdateKey: \n  XboxOneSandboxId: \n  XboxOneContentId: \n  XboxOneTitleId: \n  XboxOneSCId: \n  XboxOneGameOsOverridePath: \n  XboxOnePackagingOverridePath: \n  XboxOneAppManifestOverridePath: \n  XboxOneVersion: 1.0.0.0\n  XboxOnePackageEncryption: 0\n  XboxOnePackageUpdateGranularity: 2\n  XboxOneDescription: \n  XboxOneLanguage:\n  - enus\n  XboxOneCapability: []\n  XboxOneGameRating: {}\n  XboxOneIsContentPackage: 0\n  XboxOneEnhancedXboxCompatibilityMode: 0\n  XboxOneEnableGPUVariability: 1\n  XboxOneSockets: {}\n  XboxOneSplashScreen: {fileID: 0}\n  XboxOneAllowedProductIds: []\n  XboxOnePersistentLocalStorageSize: 0\n  XboxOneXTitleMemory: 8\n  XboxOneOverrideIdentityName: \n  XboxOneOverrideIdentityPublisher: \n  vrEditorSettings: {}\n  cloudServicesEnabled:\n    UNet: 1\n  luminIcon:\n    m_Name: \n    m_ModelFolderPath: \n    m_PortalFolderPath: \n  luminCert:\n    m_CertPath: \n    m_SignPackage: 1\n  luminIsChannelApp: 0\n  luminVersion:\n    m_VersionCode: 1\n    m_VersionName: \n  hmiPlayerDataPath: \n  hmiForceSRGBBlit: 1\n  embeddedLinuxEnableGamepadInput: 1\n  hmiLogStartupTiming: 0\n  hmiCpuConfiguration: \n  apiCompatibilityLevel: 6\n  activeInputHandler: 0\n  windowsGamepadBackendHint: 0\n  cloudProjectId: \n  framebufferDepthMemorylessMode: 0\n  qualitySettingsNames: []\n  projectName: \n  organizationId: \n  cloudEnabled: 0\n  legacyClampBlendShapeWeights: 1\n  hmiLoadingImage: {fileID: 0}\n  platformRequiresReadableAssets: 0\n  virtualTexturingSupportEnabled: 0\n  insecureHttpOption: 0\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/ProjectVersion.txt",
    "content": "m_EditorVersion: 2022.3.12f1\nm_EditorVersionWithRevision: 2022.3.12f1 (4fe6e059c7ef)\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/QualitySettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!47 &1\nQualitySettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 5\n  m_CurrentQuality: 3\n  m_QualitySettings:\n  - serializedVersion: 2\n    name: Very Low\n    pixelLightCount: 0\n    shadows: 0\n    shadowResolution: 0\n    shadowProjection: 1\n    shadowCascades: 1\n    shadowDistance: 15\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 0\n    blendWeights: 1\n    textureQuality: 1\n    anisotropicTextures: 0\n    antiAliasing: 0\n    softParticles: 0\n    softVegetation: 0\n    realtimeReflectionProbes: 0\n    billboardsFaceCameraPosition: 0\n    vSyncCount: 0\n    lodBias: 0.3\n    maximumLODLevel: 0\n    particleRaycastBudget: 4\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  - serializedVersion: 2\n    name: Low\n    pixelLightCount: 0\n    shadows: 0\n    shadowResolution: 0\n    shadowProjection: 1\n    shadowCascades: 1\n    shadowDistance: 20\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 0\n    blendWeights: 2\n    textureQuality: 0\n    anisotropicTextures: 0\n    antiAliasing: 0\n    softParticles: 0\n    softVegetation: 0\n    realtimeReflectionProbes: 0\n    billboardsFaceCameraPosition: 0\n    vSyncCount: 0\n    lodBias: 0.4\n    maximumLODLevel: 0\n    particleRaycastBudget: 16\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  - serializedVersion: 2\n    name: Medium\n    pixelLightCount: 1\n    shadows: 0\n    shadowResolution: 0\n    shadowProjection: 1\n    shadowCascades: 1\n    shadowDistance: 20\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 0\n    blendWeights: 2\n    textureQuality: 0\n    anisotropicTextures: 0\n    antiAliasing: 0\n    softParticles: 0\n    softVegetation: 0\n    realtimeReflectionProbes: 0\n    billboardsFaceCameraPosition: 0\n    vSyncCount: 1\n    lodBias: 0.7\n    maximumLODLevel: 0\n    particleRaycastBudget: 64\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  - serializedVersion: 2\n    name: High\n    pixelLightCount: 2\n    shadows: 0\n    shadowResolution: 1\n    shadowProjection: 1\n    shadowCascades: 2\n    shadowDistance: 40\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 1\n    blendWeights: 2\n    textureQuality: 0\n    anisotropicTextures: 0\n    antiAliasing: 0\n    softParticles: 0\n    softVegetation: 1\n    realtimeReflectionProbes: 0\n    billboardsFaceCameraPosition: 0\n    vSyncCount: 1\n    lodBias: 1\n    maximumLODLevel: 0\n    particleRaycastBudget: 256\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  - serializedVersion: 2\n    name: Very High\n    pixelLightCount: 3\n    shadows: 0\n    shadowResolution: 2\n    shadowProjection: 1\n    shadowCascades: 2\n    shadowDistance: 70\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 1\n    blendWeights: 4\n    textureQuality: 0\n    anisotropicTextures: 0\n    antiAliasing: 0\n    softParticles: 0\n    softVegetation: 1\n    realtimeReflectionProbes: 0\n    billboardsFaceCameraPosition: 0\n    vSyncCount: 1\n    lodBias: 1.5\n    maximumLODLevel: 0\n    particleRaycastBudget: 1024\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  - serializedVersion: 2\n    name: Ultra\n    pixelLightCount: 4\n    shadows: 0\n    shadowResolution: 0\n    shadowProjection: 1\n    shadowCascades: 4\n    shadowDistance: 150\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 1\n    blendWeights: 4\n    textureQuality: 0\n    anisotropicTextures: 0\n    antiAliasing: 0\n    softParticles: 0\n    softVegetation: 1\n    realtimeReflectionProbes: 0\n    billboardsFaceCameraPosition: 0\n    vSyncCount: 1\n    lodBias: 2\n    maximumLODLevel: 0\n    particleRaycastBudget: 4096\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  m_PerPlatformDefaultQuality:\n    Android: 2\n    Nintendo 3DS: 5\n    Nintendo Switch: 5\n    PS4: 5\n    PSM: 5\n    PSP2: 2\n    Standalone: 5\n    Tizen: 2\n    WebGL: 3\n    WiiU: 5\n    Windows Store Apps: 5\n    XboxOne: 5\n    iPhone: 2\n    tvOS: 2\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/SceneTemplateSettings.json",
    "content": "{\n    \"templatePinStates\": [],\n    \"dependencyTypeInfos\": [\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.AnimationClip\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEditor.Animations.AnimatorController\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.AnimatorOverrideController\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEditor.Audio.AudioMixerController\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.ComputeShader\",\n            \"defaultInstantiationMode\": 1\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Cubemap\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.GameObject\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEditor.LightingDataAsset\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.LightingSettings\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Material\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEditor.MonoScript\",\n            \"defaultInstantiationMode\": 1\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.PhysicMaterial\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.PhysicsMaterial2D\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Rendering.PostProcessing.PostProcessProfile\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Rendering.PostProcessing.PostProcessResources\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Rendering.VolumeProfile\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEditor.SceneAsset\",\n            \"defaultInstantiationMode\": 1\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Shader\",\n            \"defaultInstantiationMode\": 1\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.ShaderVariantCollection\",\n            \"defaultInstantiationMode\": 1\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Texture\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Texture2D\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Timeline.TimelineAsset\",\n            \"defaultInstantiationMode\": 0\n        }\n    ],\n    \"defaultDependencyTypeInfo\": {\n        \"userAdded\": false,\n        \"type\": \"<default_scene_template_dependencies>\",\n        \"defaultInstantiationMode\": 1\n    },\n    \"newSceneOverride\": 0\n}"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/TagManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!78 &1\nTagManager:\n  serializedVersion: 2\n  tags: []\n  layers:\n  - Default\n  - TransparentFX\n  - Ignore Raycast\n  - \n  - Water\n  - UI\n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  m_SortingLayers:\n  - name: Default\n    uniqueID: 0\n    locked: 0\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/TimeManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!5 &1\nTimeManager:\n  m_ObjectHideFlags: 0\n  Fixed Timestep: 0.02\n  Maximum Allowed Timestep: 0.1\n  m_TimeScale: 1\n  Maximum Particle Timestep: 0.03\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/UnityConnectSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!310 &1\nUnityConnectSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 1\n  m_Enabled: 0\n  m_TestMode: 0\n  m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events\n  m_EventUrl: https://cdp.cloud.unity3d.com/v1/events\n  m_ConfigUrl: https://config.uca.cloud.unity3d.com\n  m_DashboardUrl: https://dashboard.unity3d.com\n  m_TestInitMode: 0\n  CrashReportingSettings:\n    m_EventUrl: https://perf-events.cloud.unity3d.com\n    m_Enabled: 0\n    m_LogBufferSize: 10\n    m_CaptureEditorExceptions: 1\n  UnityPurchasingSettings:\n    m_Enabled: 0\n    m_TestMode: 0\n  UnityAnalyticsSettings:\n    m_Enabled: 0\n    m_TestMode: 0\n    m_InitializeOnStartup: 1\n    m_PackageRequiringCoreStatsPresent: 0\n  UnityAdsSettings:\n    m_Enabled: 0\n    m_InitializeOnStartup: 1\n    m_TestMode: 0\n    m_IosGameId: \n    m_AndroidGameId: \n    m_GameIds: {}\n    m_GameId: \n  PerformanceReportingSettings:\n    m_Enabled: 0\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/VFXManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!937362698 &1\nVFXManager:\n  m_ObjectHideFlags: 0\n  m_IndirectShader: {fileID: 0}\n  m_RenderPipeSettingsPath: \n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/VersionControlSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!890905787 &1\nVersionControlSettings:\n  m_ObjectHideFlags: 0\n  m_Mode: Visible Meta Files\n  m_CollabEditorSettings:\n    inProgressEnabled: 1\n"
  },
  {
    "path": "src/MasterMemory.Unity/ProjectSettings/XRSettings.asset",
    "content": "{\n    \"m_SettingKeys\": [\n        \"VR Device Disabled\",\n        \"VR Device User Alert\"\n    ],\n    \"m_SettingValues\": [\n        \"False\",\n        \"False\"\n    ]\n}"
  },
  {
    "path": "tests/MasterMemory.SourceGenerator.Tests/AssemblyAtrributeTest.cs",
    "content": "﻿namespace MasterMemory.SourceGenerator.Tests;\n\npublic class AssemblyAtrributeTest(ITestOutputHelper outputHelper) : TestBase(outputHelper)\n{\n    [Fact]\n    public void NoGeneratorOptions()\n    {\n        var codes = Helper.GenerateCode(\"\"\"\n[MemoryTable(\"item\")]\npublic class Item\n{\n    [PrimaryKey]\n    public int ItemId { get; set; }\n}\n\"\"\");\n\n        codes.TryGetValue(\"MasterMemory.DatabaseBuilder.g.cs\", out _).ShouldBeTrue();\n\n        var mainCode = codes[\"MasterMemory.ItemTable.g.cs\"];\n        WriteLine(mainCode);\n\n        mainCode.ShouldContain(\"namespace MasterMemory.Tables\");\n        mainCode.ShouldContain(\"return ThrowKeyNotFound(key);\");\n        mainCode.ShouldContain(\"public sealed partial class ItemTable\");\n    }\n\n\n    [Fact]\n    public void FullOptions()\n    {\n        var codes = Helper.GenerateCode(\"\"\"\n[assembly: MasterMemoryGeneratorOptions(\n    Namespace = \"MyNamespace\",\n    IsReturnNullIfKeyNotFound = true,\n    PrefixClassName = \"FooBarBaz\")]\n\n[MemoryTable(\"item\")]\npublic class Item\n{\n    [PrimaryKey]\n    public int ItemId { get; set; }\n}\n\"\"\");\n\n        codes.TryGetValue(\"MasterMemory.FooBarBazDatabaseBuilder.g.cs\", out _).ShouldBeTrue();\n\n        var mainCode = codes[\"MasterMemory.ItemTable.g.cs\"];\n        WriteLine(mainCode);\n\n        mainCode.ShouldContain(\"namespace MyNamespace.Tables\");\n        mainCode.ShouldNotContain(\"return ThrowKeyNotFound(key);\");\n        mainCode.ShouldContain(\"public sealed partial class ItemTable\");\n    }\n}\n"
  },
  {
    "path": "tests/MasterMemory.SourceGenerator.Tests/DiagnosticsTest.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace MasterMemory.SourceGenerator.Tests;\n\npublic class DiagnosticsTest(ITestOutputHelper outputHelper) : TestBase(outputHelper)\n{\n    [Fact]\n    public void RequirePrimaryKey()\n    {\n        Helper.Verify(1, \"\"\"\n[MemoryTable(\"item\")]\npublic class Item\n{\n    // [PrimaryKey] // No PrimaryKey\n    public int ItemId { get; set; }\n}\n\"\"\", \"Item\");\n    }\n\n    [Fact]\n    public void DuplicateSecondaryKey()\n    {\n        Helper.Verify(3, \"\"\"\n[MemoryTable(\"item\")]\npublic class Item\n{\n    [PrimaryKey]\n    public int ItemId1 { get; set; }\n    [SecondaryKey(0), SecondaryKey(1)]\n    public int ItemId2 { get; set; }\n}\n\"\"\", \"ItemId2\");\n    }\n}\n"
  },
  {
    "path": "tests/MasterMemory.SourceGenerator.Tests/GenerateTest.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Xunit.Abstractions;\n\nnamespace MasterMemory.SourceGenerator.Tests;\n\npublic class GenerateTest(ITestOutputHelper outputHelper) : TestBase(outputHelper)\n{\n    [Fact]\n    public void GenerateClass()\n    {\n        Helper.Ok(\"\"\"\n[MemoryTable(\"item\")]\npublic class Item\n{\n    [PrimaryKey]\n    public int ItemId { get; set; }\n}\n\"\"\");\n    }\n\n    [Fact]\n    public void GenerateRecord()\n    {\n        Helper.Ok(\"\"\"\n[MemoryTable(\"item\")]\npublic record Item\n{\n    [PrimaryKey]\n    public int ItemId { get; set; }\n}\n\"\"\");\n    }\n}\n"
  },
  {
    "path": "tests/MasterMemory.SourceGenerator.Tests/IncrementalGeneratorTest.cs",
    "content": "﻿namespace MasterMemory.SourceGenerator.Tests;\n\npublic class IncrementalGeneratorTest\n{\n    void VerifySourceOutputReasonIsCached((string Key, string Reasons)[] reasons)\n    {\n        var reason = reasons.FirstOrDefault(x => x.Key == \"SourceOutput\").Reasons;\n        reason.ShouldBe(\"Cached\");\n    }\n\n    void VerifySourceOutputReasonIsNotCached((string Key, string Reasons)[] reasons)\n    {\n        var reason = reasons.FirstOrDefault(x => x.Key == \"SourceOutput\").Reasons;\n        reason.ShouldNotBe(\"Cached\");\n    }\n\n    [Fact]\n    public void CheckReasons()\n    {\n        var step1 = \"\"\"\n[MemoryTable(\"item\")]\npublic class Item\n{\n    [PrimaryKey]\n    public int ItemId { get; set; }\n}\n\"\"\";\n\n        var step2 = \"\"\"\n[MemoryTable(\"item\")]\npublic class Item\n{\n    // add unrelated line\n    [PrimaryKey]\n    public int ItemId { get; set; }\n}\n\"\"\";\n\n        var step3 = \"\"\"\n[MemoryTable(\"item\")]\npublic class Item\n{\n    [PrimaryKey]\n    public int ItemId2 { get; set; } // changed name\n}\n\"\"\";\n\n        var reasons = CSharpGeneratorRunner.GetIncrementalGeneratorTrackedStepsReasons(\"MasterMemory.SyntaxProvider.\", step1, step2, step3);\n\n        VerifySourceOutputReasonIsCached(reasons[1]);\n        VerifySourceOutputReasonIsNotCached(reasons[2]);\n    }\n}\n"
  },
  {
    "path": "tests/MasterMemory.SourceGenerator.Tests/MasterMemory.SourceGenerator.Tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFramework>net9.0</TargetFramework>\n\t\t<ImplicitUsings>enable</ImplicitUsings>\n\t\t<Nullable>enable</Nullable>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.11.1\" />\n\t\t<PackageReference Include=\"Microsoft.CodeAnalysis.CSharp\" Version=\"4.9.2\" />\n\t\t<PackageReference Include=\"Shouldly\" Version=\"4.2.1\" />\n\t\t<PackageReference Include=\"xunit\" Version=\"2.9.2\" />\n\t\t<PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.8.2\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t  <ProjectReference Include=\"..\\..\\src\\MasterMemory.Annotations\\MasterMemory.Annotations.csproj\" />\n\t  <ProjectReference Include=\"..\\..\\src\\MasterMemory.SourceGenerator\\MasterMemory.SourceGenerator.csproj\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<Using Include=\"Xunit\" />\n\t\t<Using Include=\"Xunit.Abstractions\" />\n\t\t<Using Include=\"Shouldly\" />\n\t</ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "tests/MasterMemory.SourceGenerator.Tests/TestBase.cs",
    "content": "﻿namespace MasterMemory.SourceGenerator.Tests;\n\npublic abstract class TestBase(ITestOutputHelper testoutputHelper)\n{\n    protected CodeGeneratorHelper Helper = new CodeGeneratorHelper(testoutputHelper, \"MAM\");\n\n    protected void WriteLine(string message)\n    {\n        testoutputHelper.WriteLine(message);\n    }\n}\n"
  },
  {
    "path": "tests/MasterMemory.SourceGenerator.Tests/Utility/CSharpGeneratorRunner.cs",
    "content": "﻿using MasterMemory.SourceGenerator;\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.Diagnostics;\nusing System.Collections.Immutable;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.Loader;\n\npublic static class CSharpGeneratorRunner\n{\n    static Compilation baseCompilation = default!;\n\n    [ModuleInitializer]\n    public static void InitializeCompilation()\n    {\n        // Add project namespace.\n        var globalUsings = \"\"\"\nglobal using System;\nglobal using System.Threading;\nglobal using System.Threading.Tasks;\nglobal using System.ComponentModel.DataAnnotations;\nglobal using MasterMemory;\n\"\"\";\n\n        var references = AppDomain.CurrentDomain.GetAssemblies()\n            .Where(x => !x.IsDynamic && !string.IsNullOrWhiteSpace(x.Location))\n            .Select(x => MetadataReference.CreateFromFile(x.Location))\n            .Concat(GetSelfReferences());\n\n        var compilation = CSharpCompilation.Create(\"generatortest\",\n            references: references,\n            syntaxTrees: [CSharpSyntaxTree.ParseText(globalUsings, path: \"GlobalUsings.cs\")],\n            options: new CSharpCompilationOptions(OutputKind.ConsoleApplication, allowUnsafe: true)); // .exe\n\n        baseCompilation = compilation;\n\n        static IEnumerable<MetadataReference> GetSelfReferences()\n        {\n            // MasterMemory.Annotations\n            yield return MetadataReference.CreateFromFile(typeof(MasterMemory.MemoryTableAttribute).Assembly.Location);\n        }\n    }\n\n    public static (Compilation, ImmutableArray<Diagnostic>) RunGenerator([StringSyntax(\"C#-test\")] string source, string[]? preprocessorSymbols = null, AnalyzerConfigOptionsProvider? options = null)\n    {\n        if (preprocessorSymbols == null)\n        {\n            preprocessorSymbols = new[] { \"NET8_0_OR_GREATER\" };\n        }\n        var parseOptions = new CSharpParseOptions(LanguageVersion.CSharp12, preprocessorSymbols: preprocessorSymbols); // 12\n\n        // Create SourceGenerator\n        var driver = CSharpGeneratorDriver.Create(new MasterMemoryGenerator()).WithUpdatedParseOptions(parseOptions);\n        if (options != null)\n        {\n            driver = (Microsoft.CodeAnalysis.CSharp.CSharpGeneratorDriver)driver.WithUpdatedAnalyzerConfigOptions(options);\n        }\n\n        var compilation = baseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(source, parseOptions));\n\n        driver.RunGeneratorsAndUpdateCompilation(compilation, out var newCompilation, out var diagnostics);\n        return (newCompilation, diagnostics);\n    }\n\n    public static (Compilation, ImmutableArray<Diagnostic>, string) CompileAndExecute(string source, string[] args, string[]? preprocessorSymbols = null, AnalyzerConfigOptionsProvider? options = null)\n    {\n        var (compilation, diagnostics) = RunGenerator(source, preprocessorSymbols, options);\n\n        using var ms = new MemoryStream();\n        var emitResult = compilation.Emit(ms);\n        if (!emitResult.Success)\n        {\n            throw new InvalidOperationException(\"Emit Failed\\r\\n\" + string.Join(\"\\r\\n\", emitResult.Diagnostics.Select(x => x.ToString())));\n        }\n\n        ms.Position = 0;\n\n        // capture stdout log\n        // modify global stdout so can't run in parallel unit-test\n        var originalOut = Console.Out;\n        try\n        {\n            var stringWriter = new StringWriter();\n            Console.SetOut(stringWriter);\n\n            // load and invoke Main(args)\n            var loadContext = new AssemblyLoadContext(\"source-generator\", isCollectible: true); // isCollectible to support Unload\n            var assembly = loadContext.LoadFromStream(ms);\n            assembly.EntryPoint!.Invoke(null, new object[] { args });\n            loadContext.Unload();\n\n            return (compilation, diagnostics, stringWriter.ToString());\n        }\n        finally\n        {\n            Console.SetOut(originalOut);\n        }\n    }\n\n    public static (string Key, string Reasons)[][] GetIncrementalGeneratorTrackedStepsReasons(string keyPrefixFilter, params string[] sources)\n    {\n        var parseOptions = new CSharpParseOptions(LanguageVersion.CSharp12); // 12\n        var driver = CSharpGeneratorDriver.Create(\n            [new MasterMemoryGenerator().AsSourceGenerator()],\n            driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true))\n            .WithUpdatedParseOptions(parseOptions);\n\n        var generatorResults = sources\n            .Select(source =>\n            {\n                var compilation = baseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(source, parseOptions));\n                driver = driver.RunGenerators(compilation);\n                return driver.GetRunResult().Results[0];\n            })\n            .ToArray();\n\n        var reasons = generatorResults\n            .Select(x => x.TrackedSteps\n                .Where(x => x.Key.StartsWith(keyPrefixFilter) || x.Key == \"SourceOutput\")\n                .Select(x =>\n                {\n                    if (x.Key == \"SourceOutput\")\n                    {\n                        var values = x.Value.Where(x => x.Inputs[0].Source.Name?.StartsWith(keyPrefixFilter) ?? false);\n                        return (\n                            x.Key,\n                            Reasons: string.Join(\", \", values.SelectMany(x => x.Outputs).Select(x => x.Reason).ToArray())\n                        );\n                    }\n                    else\n                    {\n                        return (\n                            Key: x.Key.Substring(keyPrefixFilter.Length),\n                            Reasons: string.Join(\", \", x.Value.SelectMany(x => x.Outputs).Select(x => x.Reason).ToArray())\n                        );\n                    }\n                })\n                .OrderBy(x => x.Key)\n                .ToArray())\n            .ToArray();\n\n        return reasons;\n    }\n}\n"
  },
  {
    "path": "tests/MasterMemory.SourceGenerator.Tests/Utility/CodeGeneratorHelper.cs",
    "content": "﻿using Microsoft.CodeAnalysis;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Runtime.CompilerServices;\n\npublic class CodeGeneratorHelper(ITestOutputHelper output, string idPrefix)\n{\n    // Diagnostics Verify\n\n    public void Ok([StringSyntax(\"C#-test\")] string code, [CallerArgumentExpression(\"code\")] string? codeExpr = null)\n    {\n        output.WriteLine(codeExpr);\n\n        var (compilation, diagnostics) = CSharpGeneratorRunner.RunGenerator(code);\n        foreach (var item in diagnostics)\n        {\n            output.WriteLine(item.ToString());\n        }\n        OutputGeneratedCode(compilation);\n\n        diagnostics.Length.ShouldBe(0);\n    }\n\n    public Dictionary<string, string> GenerateCode([StringSyntax(\"C#-test\")] string code, [CallerArgumentExpression(\"code\")] string? codeExpr = null)\n    {\n        var (compilation, diagnostics) = CSharpGeneratorRunner.RunGenerator(code);\n        foreach (var item in diagnostics)\n        {\n            output.WriteLine(item.ToString());\n        }\n        diagnostics.Length.ShouldBe(0);\n\n        var dict = new Dictionary<string, string>();\n        foreach (var syntaxTree in compilation.SyntaxTrees)\n        {\n            // only shows ConsoleApp.Run/Builder generated code\n            if (!syntaxTree.FilePath.Contains(\"g.cs\")) continue;\n            var generatedCode = syntaxTree.ToString();\n            var fileName = Path.GetFileName(syntaxTree.FilePath);\n            dict.Add(fileName, generatedCode);\n        }\n\n        return dict;\n    }\n\n    public void Verify(int id, [StringSyntax(\"C#-test\")] string code, string diagnosticsCodeSpan, [CallerArgumentExpression(\"code\")] string? codeExpr = null)\n    {\n        output.WriteLine(codeExpr);\n\n        var (compilation, diagnostics) = CSharpGeneratorRunner.RunGenerator(code);\n        foreach (var item in diagnostics)\n        {\n            output.WriteLine(item.ToString());\n        }\n        OutputGeneratedCode(compilation);\n\n        diagnostics.Length.ShouldBe(1);\n        diagnostics[0].Id.ShouldBe(idPrefix + id.ToString(\"000\"));\n\n        var text = GetLocationText(diagnostics[0], compilation.SyntaxTrees);\n        text.ShouldBe(diagnosticsCodeSpan);\n    }\n\n    public (string, string)[] Verify([StringSyntax(\"C#-test\")] string code, [CallerArgumentExpression(\"code\")] string? codeExpr = null)\n    {\n        output.WriteLine(codeExpr);\n\n        var (compilation, diagnostics) = CSharpGeneratorRunner.RunGenerator(code);\n        OutputGeneratedCode(compilation);\n        return diagnostics.Select(x => (x.Id, GetLocationText(x, compilation.SyntaxTrees))).ToArray();\n    }\n\n    // Execute and check stdout result\n\n    public void Execute([StringSyntax(\"C#-test\")] string code, string args, string expected, [CallerArgumentExpression(\"code\")] string? codeExpr = null)\n    {\n        output.WriteLine(codeExpr);\n\n        var (compilation, diagnostics, stdout) = CSharpGeneratorRunner.CompileAndExecute(code, args == \"\" ? [] : args.Split(' '));\n        foreach (var item in diagnostics)\n        {\n            output.WriteLine(item.ToString());\n        }\n        OutputGeneratedCode(compilation);\n\n        stdout.ShouldBe(expected);\n    }\n\n    public string Error([StringSyntax(\"C#-test\")] string code, string args, [CallerArgumentExpression(\"code\")] string? codeExpr = null)\n    {\n        output.WriteLine(codeExpr);\n\n        var (compilation, diagnostics, stdout) = CSharpGeneratorRunner.CompileAndExecute(code, args == \"\" ? [] : args.Split(' '));\n        foreach (var item in diagnostics)\n        {\n            output.WriteLine(item.ToString());\n        }\n        OutputGeneratedCode(compilation);\n\n        return stdout;\n    }\n\n    string GetLocationText(Diagnostic diagnostic, IEnumerable<SyntaxTree> syntaxTrees)\n    {\n        var location = diagnostic.Location;\n\n        var textSpan = location.SourceSpan;\n        var sourceTree = location.SourceTree;\n        if (sourceTree == null)\n        {\n            var lineSpan = location.GetLineSpan();\n            if (lineSpan.Path == null) return \"\";\n\n            sourceTree = syntaxTrees.FirstOrDefault(x => x.FilePath == lineSpan.Path);\n            if (sourceTree == null) return \"\";\n        }\n\n        var text = sourceTree.GetText().GetSubText(textSpan).ToString();\n        return text;\n    }\n\n    void OutputGeneratedCode(Compilation compilation)\n    {\n        foreach (var syntaxTree in compilation.SyntaxTrees)\n        {\n            // only shows ConsoleApp.Run/Builder generated code\n            if (!syntaxTree.FilePath.Contains(\"g.cs\")) continue;\n            output.WriteLine(syntaxTree.ToString());\n        }\n    }\n}"
  },
  {
    "path": "tests/MasterMemory.Tests/BinarySearchTest.cs",
    "content": "﻿using MasterMemory.Internal;\nusing MessagePack.Resolvers;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Xunit;\nusing MessagePack;\n\nnamespace MasterMemory.Tests\n{\n    public class BinarySearchTest\n    {\n        public BinarySearchTest()\n        {\n            MessagePackSerializer.DefaultOptions = MessagePackSerializer.DefaultOptions.WithResolver(MessagePackResolver.Instance);\n        }\n\n        [Fact]\n        public void Find()\n        {\n            var rand = new Random();\n            for (int iii = 0; iii < 30; iii++)\n            {\n                var seed = Enumerable.Range(1, 10);\n                var randomSeed = seed.Where(x => rand.Next() % 2 == 0);\n\n                var array = randomSeed.Concat(randomSeed).Concat(randomSeed).Concat(randomSeed)\n                    .OrderBy(x => x)\n                    .ToArray();\n\n                for (int i = 1; i <= 10; i++)\n                {\n                    var firstIndex = Array.IndexOf(array, i);\n                    var lastIndex = Array.LastIndexOf(array, i);\n\n                    var f = BinarySearch.FindFirst(array, i, x => x, Comparer<int>.Default);\n                    var l = BinarySearch.LowerBound(array, 0, array.Length, i, x => x, Comparer<int>.Default);\n                    var u = BinarySearch.UpperBound(array, 0, array.Length, i, x => x, Comparer<int>.Default);\n\n                    // not found\n                    if (firstIndex == -1)\n                    {\n                        f.ShouldBe(-1);\n                        l.ShouldBe(-1);\n                        u.ShouldBe(-1);\n                        continue;\n                    }\n\n                    array[f].ShouldBe(i);\n                    array[l].ShouldBe(i);\n                    array[u].ShouldBe(i);\n\n                    l.ShouldBe(firstIndex);\n                    u.ShouldBe(lastIndex);\n                }\n            }\n\n            // and empty\n            var emptyArray = Enumerable.Empty<int>().ToArray();\n            BinarySearch.FindFirst(emptyArray, 0, x => x, Comparer<int>.Default).ShouldBe(-1);\n            BinarySearch.LowerBound(emptyArray, 0, emptyArray.Length, 0, x => x, Comparer<int>.Default).ShouldBe(-1);\n            BinarySearch.UpperBound(emptyArray, 0, emptyArray.Length, 0, x => x, Comparer<int>.Default).ShouldBe(-1);\n        }\n\n        [Fact]\n        public void Closest()\n        {\n            // empty\n            var array = Enumerable.Empty<int>().ToArray();\n\n            var near = BinarySearch.FindClosest(array, 0, 0, array.Length, x => x, Comparer<int>.Default, false);\n            near.ShouldBe(-1);\n\n            // mid\n            var source = new[]{\n                new { id = 0, bound = 0 },\n                new { id = 1, bound = 100 },\n                new { id = 2, bound = 200 },\n                new { id = 3, bound = 300 },\n                new { id = 4, bound = 500 },\n                new { id = 5, bound = 1000 },\n            };\n\n            BinarySearch.FindClosest(source, 0, source.Length, -100, x => x.bound, Comparer<int>.Default, true).ShouldBe(-1);\n//          BinarySearch.FindClosest(source, 0, source.Length, -100, x => x.bound, Comparer<int>.Default, true).ShouldBe(0);\n            BinarySearch.FindClosest(source, 0, source.Length, 0, x => x.bound, Comparer<int>.Default, true).ShouldBe(0);\n            BinarySearch.FindClosest(source, 0, source.Length, 10, x => x.bound, Comparer<int>.Default, true).ShouldBe(0);\n            BinarySearch.FindClosest(source, 0, source.Length, 50, x => x.bound, Comparer<int>.Default, true).ShouldBe(0);\n\n            source[BinarySearch.FindClosest(source, 0, source.Length, 100, x => x.bound, Comparer<int>.Default, true)].id.ShouldBe(1);\n            source[BinarySearch.FindClosest(source, 0, source.Length, 100, x => x.bound, Comparer<int>.Default, false)].id.ShouldBe(1);\n\n            source[BinarySearch.FindClosest(source, 0, source.Length, 150, x => x.bound, Comparer<int>.Default, true)].id.ShouldBe(1);\n            source[BinarySearch.FindClosest(source, 0, source.Length, 300, x => x.bound, Comparer<int>.Default, true)].id.ShouldBe(3);\n            source[BinarySearch.FindClosest(source, 0, source.Length, 999, x => x.bound, Comparer<int>.Default, true)].id.ShouldBe(4);\n            source[BinarySearch.FindClosest(source, 0, source.Length, 1000, x => x.bound, Comparer<int>.Default, true)].id.ShouldBe(5);\n            source[BinarySearch.FindClosest(source, 0, source.Length, 1001, x => x.bound, Comparer<int>.Default, true)].id.ShouldBe(5);\n            source[BinarySearch.FindClosest(source, 0, source.Length, 10000, x => x.bound, Comparer<int>.Default, true)].id.ShouldBe(5);\n//          source[BinarySearch.FindClosest(source, 0, source.Length, 10000, x => x.bound, Comparer<int>.Default, false)].id.ShouldBe(5);\n \n            BinarySearch.FindClosest(source, 0, source.Length, 10000, x => x.bound, Comparer<int>.Default, false).ShouldBe(6);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/MasterMemory.Tests/DatabaseTest.cs",
    "content": "﻿#pragma warning disable\nusing MessagePack;\nusing MessagePack.Resolvers;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace MasterMemory.Tests\n{\n    public class DatabaseTest\n    {\n        public DatabaseTest()\n        {\n            MessagePackSerializer.DefaultOptions = MessagePackSerializer.DefaultOptions.WithResolver(MessagePackResolver.Instance);\n        }\n\n        Sample[] CreateData()\n        {\n            // Id = Unique, PK\n            // FirstName + LastName = Unique\n            var data = new[]\n            {\n                new Sample { Id = 5, Age = 19, FirstName = \"aaa\", LastName = \"foo\" },\n                new Sample { Id = 6, Age = 29, FirstName = \"bbb\", LastName = \"foo\" },\n                new Sample { Id = 7, Age = 39, FirstName = \"ccc\", LastName = \"foo\" },\n                new Sample { Id = 8, Age = 49, FirstName = \"ddd\", LastName = \"foo\" },\n                new Sample { Id = 1, Age = 59, FirstName = \"eee\", LastName = \"foo\" },\n                new Sample { Id = 2, Age = 89, FirstName = \"aaa\", LastName = \"bar\" },\n                new Sample { Id = 3, Age = 79, FirstName = \"be\", LastName = \"de\" },\n                new Sample { Id = 4, Age = 89, FirstName = \"aaa\", LastName = \"tako\" },\n                new Sample { Id = 9, Age = 99, FirstName = \"aaa\", LastName = \"ika\" },\n                new Sample { Id = 10, Age = 9, FirstName = \"eee\", LastName = \"baz\" },\n            };\n            return data;\n        }\n\n        [Fact]\n        public void SingleDb()\n        {\n            var builder = new DatabaseBuilder();\n            builder.Append(CreateData());\n\n            var bin = builder.Build();\n            var db = new MemoryDatabase(bin);\n            db.SampleTable.FindById(8).Age.ShouldBe(49);\n\n            var tableInfo = MemoryDatabase.GetTableInfo(bin);\n            tableInfo[0].TableName.ShouldBe(\"s_a_m_p_l_e\");\n        }\n\n        [Fact]\n        public void All()\n        {\n            var builder = new DatabaseBuilder();\n            builder.Append(CreateData());\n\n            var bin = builder.Build();\n            var db = new MemoryDatabase(bin);\n\n            db.SampleTable.All.Select(x => x.Id).ToArray().ShouldBeEquivalentTo(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });\n            db.SampleTable.AllReverse.Select(x => x.Id).ToArray().ShouldBeEquivalentTo(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }.Reverse().ToArray());\n            db.SampleTable.SortByAge.Select(x => x.Id).OrderBy(x => x).ToArray().ShouldBeEquivalentTo(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });\n        }\n\n        [Fact]\n        public void Ranges()\n        {\n            var builder = new DatabaseBuilder();\n            builder.Append(CreateData());\n\n            var bin = builder.Build();\n            var db = new MemoryDatabase(bin);\n\n            db.SampleTable.FindRangeByAge(2, 2).Select(x => x.Id).ToArray().ShouldBeEquivalentTo(new int[] { });\n            db.SampleTable.FindRangeByAge(30, 50).Select(x => x.Id).ToArray().ShouldBeEquivalentTo(new int[] { 7, 8 });\n            db.SampleTable.FindRangeByAge(100, 100).Select(x => x.Id).ToArray().ShouldBeEquivalentTo(new int[] { });\n        }\n\n\n        [Fact]\n        public void EmptyAll()\n        {\n            {\n                var builder = new DatabaseBuilder();\n                builder.Append(new Sample[] { });\n\n                var bin = builder.Build();\n                var db = new MemoryDatabase(bin);\n\n                db.SampleTable.All.Any().ShouldBeFalse();\n            }\n            {\n                var builder = new DatabaseBuilder();\n                builder.Append(new Sample[] { }.Select(x => x));\n\n                var bin = builder.Build();\n                var db = new MemoryDatabase(bin);\n\n                db.SampleTable.All.Any().ShouldBeFalse();\n            }\n        }\n\n        [Fact]\n        public void WithNull()\n        {\n            var builder = new DatabaseBuilder();\n            builder.Append(new Sample[] {new Sample\n            {\n                Age = 10,\n                FirstName = null,\n                Id = 999,\n                LastName = \"abcde\"\n            } });\n\n            var bin = builder.Build();\n            var db = new MemoryDatabase(bin);\n\n            var sample = db.SampleTable.FindById(999);\n            sample.Age.ShouldBe(10);\n            sample.FirstName.ShouldBeNull();\n            sample.LastName.ShouldBe(\"abcde\");\n        }\n    }\n}\n"
  },
  {
    "path": "tests/MasterMemory.Tests/IssueTest.cs",
    "content": "﻿using MasterMemory.Tests.TestStructures;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Xunit;\n\nnamespace MasterMemory.Tests\n{\n    public class IssueTest\n    {\n        //[Fact]\n        //public void Issue49()\n        //{\n        //    var builder = new DatabaseBuilder().Append(new[]\n        //    {\n        //        new PersonModel { FirstName = \"realname\", LastName=\"reallast\" },\n        //        new PersonModel { FirstName = \"fakefirst\", LastName=\"fakelast\" },\n        //    });\n\n        //    var data = builder.Build();\n        //    var database = new MemoryDatabase(data);\n\n        //    var entries = database.PersonModelTable.FindClosestByFirstNameAndLastName((\"real\", \"real\"), false);\n        //    var firstEntry = entries.FirstOrDefault();\n\n        //    var firstIs = firstEntry.FirstName;\n\n        //}\n\n        Sample[] CreateData()\n        {\n            // Id = Unique, PK\n            // FirstName + LastName = Unique\n            var data = new[]\n            {\n                new Sample { Id = 5, Age = 19, FirstName = \"aaa\", LastName = \"foo\" },\n                new Sample { Id = 6, Age = 29, FirstName = \"bbb\", LastName = \"foo\" },\n                new Sample { Id = 7, Age = 39, FirstName = \"ccc\", LastName = \"foo\" },\n                new Sample { Id = 8, Age = 49, FirstName = \"ddd\", LastName = \"foo\" },\n                new Sample { Id = 1, Age = 59, FirstName = \"eee\", LastName = \"foo\" },\n                new Sample { Id = 2, Age = 89, FirstName = \"aaa\", LastName = \"bar\" },\n                new Sample { Id = 3, Age = 79, FirstName = \"be\", LastName = \"de\" },\n                new Sample { Id = 4, Age = 89, FirstName = \"aaa\", LastName = \"tako\" },\n                new Sample { Id = 9, Age = 99, FirstName = \"aaa\", LastName = \"ika\" },\n                new Sample { Id = 10, Age = 9, FirstName = \"eee\", LastName = \"baz\" },\n            };\n            return data;\n        }\n\n        [Fact]\n        public void Issue57()\n        {\n            var builder = new DatabaseBuilder();\n            builder.Append(CreateData());\n\n            var bin = builder.Build();\n            var db = new MemoryDatabase(bin);\n\n            db.SampleTable.FindRangeByAge(2, 2).Select(x => x.Id).ToArray().ShouldBeEquivalentTo(new int[] { });\n            db.SampleTable.FindRangeByAge(30, 50).Select(x => x.Id).ToArray().ShouldBeEquivalentTo(new int[] { 7, 8 });\n            db.SampleTable.FindRangeByAge(100, 100).Select(x => x.Id).ToArray().ShouldBeEquivalentTo(new int[] { });\n        }\n\n    }\n}\n"
  },
  {
    "path": "tests/MasterMemory.Tests/MasterMemory.Tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\n\t\t<TargetFramework>net9.0</TargetFramework>\n\t</PropertyGroup>\n\n\t<ItemGroup>\n\t\t<PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.11.1\" />\n\t\t<PackageReference Include=\"Shouldly\" Version=\"4.2.1\" />\n\t\t<PackageReference Include=\"xunit\" Version=\"2.9.2\" />\n\t\t<PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.8.2\" />\n\t</ItemGroup>\n\n\t<ItemGroup>\n\t\t<ProjectReference Include=\"..\\..\\src\\MasterMemory\\MasterMemory.csproj\" />\n\t\t<ProjectReference Include=\"..\\..\\src\\MasterMemory.SourceGenerator\\MasterMemory.SourceGenerator.csproj\">\n\t\t\t<OutputItemType>Analyzer</OutputItemType>\n\t\t\t<ReferenceOutputAssembly>false</ReferenceOutputAssembly>\n\t\t</ProjectReference>\n\t</ItemGroup>\n\t<ItemGroup>\n\t\t<Using Include=\"Xunit\" />\n\t\t<Using Include=\"Xunit.Abstractions\" />\n\t\t<Using Include=\"Shouldly\" />\n\t</ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "tests/MasterMemory.Tests/MemoryKeyTest.cs",
    "content": "﻿#pragma warning disable\nusing Xunit;\nusing System.Linq;\nusing MasterMemory.Tests.Tables;\nusing MessagePack;\nusing System.Collections.Generic;\n\nnamespace MasterMemory.Tests\n{\n    public class MemoryKeyMemoryTest\n    {\n        public MemoryKeyMemoryTest()\n        {\n            MessagePackSerializer.DefaultOptions = MessagePackSerializer.DefaultOptions.WithResolver(MessagePackResolver.Instance);\n        }\n\n        Sample[] CreateData()\n        {\n            // Id = Unique, PK\n            // FirstName + LastName = Unique\n            var data = new[]\n            {\n                new Sample { Id = 5, Age = 19, FirstName = \"aaa\", LastName = \"foo\" },\n                new Sample { Id = 6, Age = 29, FirstName = \"bbb\", LastName = \"foo\" },\n                new Sample { Id = 7, Age = 39, FirstName = \"ccc\", LastName = \"foo\" },\n                new Sample { Id = 8, Age = 49, FirstName = \"ddd\", LastName = \"foo\" },\n                new Sample { Id = 1, Age = 59, FirstName = \"eee\", LastName = \"foo\" },\n                new Sample { Id = 2, Age = 89, FirstName = \"aaa\", LastName = \"bar\" },\n                new Sample { Id = 3, Age = 79, FirstName = \"be\", LastName = \"de\" },\n                new Sample { Id = 4, Age = 89, FirstName = \"aaa\", LastName = \"tako\" },\n                new Sample { Id = 9, Age = 99, FirstName = \"aaa\", LastName = \"ika\" },\n                new Sample { Id = 10, Age = 9, FirstName = \"eee\", LastName = \"baz\" },\n            };\n            return data;\n        }\n\n        SampleTable CreateTable()\n        {\n            return new MemoryDatabase(new DatabaseBuilder().Append(CreateData()).Build()).SampleTable;\n        }\n\n        [Fact]\n        public void Unique()\n        {\n            var table = CreateTable();\n\n            table.FindById(8).Id.ShouldBe(8);\n            Assert.Throws<KeyNotFoundException>(() => table.FindById(100));\n\n            table.FindByIdAndAge((4, 89)).Id.ShouldBe(4);\n\n            Assert.Throws<KeyNotFoundException>(() => table.FindByIdAndAge((4, 899)));\n            Assert.Throws<KeyNotFoundException>(() => table.FindByIdAndAge((5, 89)));\n\n            table.FindByIdAndAgeAndFirstName((6, 29, \"bbb\")).Id.ShouldBe(6);\n            Assert.Throws<KeyNotFoundException>(() => table.FindByIdAndAgeAndFirstName((6, 29, \"bbbz\")));\n        }\n\n        [Fact]\n        public void Range()\n        {\n            var table = CreateTable();\n\n            var test = table.FindByFirstName(\"eee\");\n\n            table.FindByFirstName(\"eee\").Select(x => x.Id).OrderBy(x => x).ToArray().ShouldBeEquivalentTo(new[] { 1, 10 });\n            table.FindByFirstName(\"eeee\").Count.ShouldBe(0);\n\n            table.FindClosestByFirstNameAndLastName((\"aaa\", \"takz\")).Id.ShouldBe(4);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/MasterMemory.Tests/MemoryTest.cs",
    "content": "﻿using MasterMemory.Tests.Tables;\nusing MessagePack;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Xunit;\n\nnamespace MasterMemory.Tests\n{\n    public class MemoryTest\n    {\n        public MemoryTest()\n        {\n            MessagePackSerializer.DefaultOptions = MessagePackSerializer.DefaultOptions.WithResolver(MessagePackResolver.Instance);\n        }\n\n        Sample[] CreateData()\n        {\n            // Id = Unique, PK\n            // FirstName + LastName = Unique\n            var data = new[]\n            {\n                new Sample { Id = 5, Age = 19, FirstName = \"aaa\", LastName = \"foo\" },\n                new Sample { Id = 6, Age = 29, FirstName = \"bbb\", LastName = \"foo\" },\n                new Sample { Id = 7, Age = 39, FirstName = \"ccc\", LastName = \"foo\" },\n                new Sample { Id = 8, Age = 49, FirstName = \"ddd\", LastName = \"foo\" },\n                new Sample { Id = 1, Age = 59, FirstName = \"eee\", LastName = \"foo\" },\n                new Sample { Id = 2, Age = 89, FirstName = \"aaa\", LastName = \"bar\" },\n                new Sample { Id = 3, Age = 79, FirstName = \"be\", LastName = \"de\" },\n                new Sample { Id = 4, Age = 89, FirstName = \"aaa\", LastName = \"tako\" },\n                new Sample { Id = 9, Age = 99, FirstName = \"aaa\", LastName = \"ika\" },\n                new Sample { Id = 10, Age = 9, FirstName = \"eee\", LastName = \"baz\" },\n            };\n            return data;\n        }\n\n        SampleTable CreateTable(Sample[] data)\n        {\n            return new MemoryDatabase(new DatabaseBuilder().Append(data).Build()).SampleTable;\n        }\n\n        [Fact]\n        public void Count()\n        {\n            var data = CreateData();\n            var table = CreateTable(data);\n\n            table.Count.ShouldBe(data.Length);\n        }\n\n        [Fact]\n        public void Find()\n        {\n            var data = CreateData();\n            var table = CreateTable(data);\n\n            foreach (var item in data)\n            {\n                var f = table.FindById(item.Id);\n                item.Id.ShouldBe(f.Id);\n            }\n\n            Assert.Throws<KeyNotFoundException>(() => table.FindById(110));\n            table.TryFindById(110, out _).ShouldBeFalse();\n        }\n\n        [Fact]\n        public void MultiKeyFind()\n        {\n            var data = CreateData();\n            var table = CreateTable(data);\n\n            foreach (var item in data)\n            {\n                var f = table.FindByFirstNameAndLastName((item.FirstName, item.LastName));\n                item.Id.ShouldBe(f.Id);\n            }\n\n            Assert.Throws<KeyNotFoundException>(() => table.FindByFirstNameAndLastName((\"aaa\", \"___\")));\n            Assert.Throws<KeyNotFoundException>(() => table.FindByFirstNameAndLastName((\"___\", \"foo\")));\n            table.TryFindByFirstNameAndLastName((\"aaa\", \"___\"), out _).ShouldBeFalse();\n            table.TryFindByFirstNameAndLastName((\"___\", \"foo\"), out _).ShouldBeFalse();\n        }\n\n        [Fact]\n        public void FindClosest()\n        {\n            var data = CreateData();\n            var table = CreateTable(data);\n\n            {\n                table.FindClosestByAge(56, true).First.Age.ShouldBe(49);\n                table.FindClosestByAge(56, false).First.Age.ShouldBe(59);\n            }\n            {\n                // first\n                for (int i = 0; i < 9; i++)\n                {\n                    table.FindClosestByAge(i, selectLower: true).Count.ShouldBe(0);\n//                  table.FindClosestByAge(i, selectLower: true).First.Age.ShouldBe(9);\n                }\n\n                var lastAge = 9;\n                foreach (var item in data.OrderBy(x => x.Age))\n                {\n                    for (int i = lastAge + 1; i < item.Age; i++)\n                    {\n                        table.FindClosestByAge(i, selectLower: true).First.Age.ShouldBe(lastAge);\n                    }\n\n                    lastAge = item.Age;\n                }\n\n                // last\n                table.FindClosestByAge(99, selectLower: false).First.Age.ShouldBe(99);\n\n                for (int i = 100; i < 120; i++)\n                {\n                    table.FindClosestByAge(i, selectLower: false).Count.ShouldBe(0);\n//                  table.FindClosestByAge(i, selectLower: true).First.Age.ShouldBe(99);\n                }\n            }\n            {\n                // first\n                for (int i = 0; i < 9; i++)\n                {\n                    table.FindClosestByAge(i, selectLower: false).First.Age.ShouldBe(9);\n                }\n\n                var xss = data.OrderBy(x => x.Age).ToArray();\n                for (int j = 1; j < xss.Length - 1; j++)\n                {\n                    var item = xss[j];\n                    for (int i = xss[j - 1].Age + 1; i < item.Age; i++)\n                    {\n                        table.FindClosestByAge(i, selectLower: false).First.Age.ShouldBe(xss[j].Age);\n                    }\n                }\n\n                // last\n                table.FindClosestByAge(99, selectLower: false).First.Age.ShouldBe(99);\n\n                for (int i = 100; i < 120; i++)\n                {\n                    table.FindClosestByAge(i, selectLower: false).Count.ShouldBe(0);\n                }\n            }\n        }\n\n        [Fact]\n        public void FindClosestMultiKey()\n        {\n            var data = CreateData();\n            var table = CreateTable(data);\n\n            // Age of aaa\n            //new Sample { Id = 5, Age = 19, FirstName = \"aaa\", LastName = \"foo\" },\n            //new Sample { Id = 2, Age = 89, FirstName = \"aaa\", LastName = \"bar\" },\n            //new Sample { Id = 4, Age = 89, FirstName = \"aaa\", LastName = \"tako\" },\n            //new Sample { Id = 9, Age = 99, FirstName = \"aaa\", LastName = \"ika\" },\n\n            table.FindClosestByFirstNameAndAge((\"aaa\", 10), true).Count.ShouldBe(0);\n            table.FindClosestByFirstNameAndAge((\"aaa\", 10), false).First.Age.ShouldBe(19);\n            table.FindClosestByFirstNameAndAge((\"aaa\", 92), true).First.Age.ShouldBe(89);\n            table.FindClosestByFirstNameAndAge((\"aaa\", 120), true).First.Age.ShouldBe(99);\n            table.FindClosestByFirstNameAndAge((\"aaa\", 10), false).First.Age.ShouldBe(19);\n            table.FindClosestByFirstNameAndAge((\"aaa\", 73), false).First.Age.ShouldBe(89);\n        }\n\n        [Fact]\n        public void FindMany()\n        {\n            var data = CreateData();\n            var table = CreateTable(data);\n\n            table.FindByFirstName(\"aaa\").OrderBy(x => x.Id).Select(x => x.Id).ToArray().ShouldBeEquivalentTo(new[] { 2, 4, 5, 9 });\n        }\n\n        [Fact]\n        public void FindManyMultiKey()\n        {\n            var data = CreateData();\n            var table = CreateTable(data);\n\n            table.FindByFirstNameAndAge((\"aaa\", 89)).Select(x => x.Id).ToArray().ShouldBeEquivalentTo(new[] { 2, 4 });\n            table.FindByFirstNameAndAge((\"aaa\", 89)).Reverse.Select(x => x.Id).ToArray().ShouldBeEquivalentTo(new[] { 4, 2 });\n        }\n    }\n}"
  },
  {
    "path": "tests/MasterMemory.Tests/MessagePackResolver.cs",
    "content": "﻿using MessagePack;\nusing MessagePack.Resolvers;\n\nnamespace MasterMemory.Tests;\n\n[CompositeResolver(typeof(MasterMemoryResolver), typeof(StandardResolver))]\npublic partial class MessagePackResolver;"
  },
  {
    "path": "tests/MasterMemory.Tests/MetaTest.cs",
    "content": "﻿#pragma warning disable\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing Xunit;\n\nnamespace MasterMemory.Tests\n{\n    public class MetaTest\n    {\n        [Fact]\n        public void Meta()\n        {\n            var metaDb = MemoryDatabase.GetMetaDatabase();\n\n            var sampleTable = metaDb.GetTableInfo(\"s_a_m_p_l_e\");\n\n            sampleTable.TableName.ShouldBe(\"s_a_m_p_l_e\");\n\n            sampleTable.Properties[0].Name.ShouldBe(\"Id\");\n            sampleTable.Properties[0].NameLowerCamel.ShouldBe(\"id\");\n            sampleTable.Properties[0].NameSnakeCase.ShouldBe(\"id\");\n\n            sampleTable.Properties[2].Name.ShouldBe(\"FirstName\");\n            sampleTable.Properties[2].NameLowerCamel.ShouldBe(\"firstName\");\n            sampleTable.Properties[2].NameSnakeCase.ShouldBe(\"first_name\");\n\n            var primary = sampleTable.Indexes[0];\n            primary.IsUnique.ShouldBeTrue();\n            primary.IndexProperties[0].Name.ShouldBe(\"Id\");\n        }\n    }\n}\n"
  },
  {
    "path": "tests/MasterMemory.Tests/RangeViewTest.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Xunit;\n\nnamespace MasterMemory.Tests\n{\n    public class RangeViewTest\n    {\n        [Fact]\n        public void Range()\n        {\n            // 4 -> 8\n            {\n                var range = new RangeView<int>(new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 4, 8, true);\n\n                range.Count.ShouldBe(5);\n                range[0].ShouldBe(4);\n                range[1].ShouldBe(5);\n                range[2].ShouldBe(6);\n                range[3].ShouldBe(7);\n                range[4].ShouldBe(8);\n\n                Assert.Throws<ArgumentOutOfRangeException>(() => range[-1]);\n                Assert.Throws<ArgumentOutOfRangeException>(() => range[5]);\n\n                var begin = 4;\n                foreach (var item in range)\n                {\n                    item.ShouldBe(begin++);\n                }\n\n                var xs = new int[10];\n                range.CopyTo(xs, 3);\n                xs[3].ShouldBe(4);\n                xs[4].ShouldBe(5);\n                xs[5].ShouldBe(6);\n                xs[6].ShouldBe(7);\n                xs[7].ShouldBe(8);\n                xs[8].ShouldBe(0);\n\n                range.IndexOf(5).ShouldBe(1);\n                range.IndexOf(9).ShouldBe(-1);\n\n\n                range.Contains(5).ShouldBeTrue();\n                range.Contains(9).ShouldBeFalse();\n            }\n            {\n                // 7 -> 2\n                var range = new RangeView<int>(new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7, false);\n\n                range.Count.ShouldBe(6);\n                range[0].ShouldBe(7);\n                range[1].ShouldBe(6);\n                range[2].ShouldBe(5);\n                range[3].ShouldBe(4);\n                range[4].ShouldBe(3);\n                range[5].ShouldBe(2);\n\n                Assert.Throws<ArgumentOutOfRangeException>(() => range[-1]);\n                Assert.Throws<ArgumentOutOfRangeException>(() => range[6]);\n\n                var begin = 7;\n                foreach (var item in range)\n                {\n                    item.ShouldBe(begin--);\n                }\n\n                var xs = new int[10];\n                range.CopyTo(xs, 3);\n                xs[3].ShouldBe(7);\n                xs[4].ShouldBe(6);\n                xs[5].ShouldBe(5);\n                xs[6].ShouldBe(4);\n                xs[7].ShouldBe(3);\n                xs[8].ShouldBe(2);\n\n                range.IndexOf(5).ShouldBe(2);\n                range.IndexOf(9).ShouldBe(-1);\n\n                range.Contains(5).ShouldBeTrue();\n                range.Contains(9).ShouldBeFalse();\n            }\n\n            var empty = new RangeView<int>(Enumerable.Empty<int>().ToArray(), 0, 0, true);\n            empty.Count.ShouldBe(0);\n\n            var same = new RangeView<int>(Enumerable.Range(1, 1000).ToArray(), 100, 100, true);\n            same.Count.ShouldBe(1);\n            same[0].ShouldBe(101);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/MasterMemory.Tests/TestStructures/PersonModel.cs",
    "content": "﻿#pragma warning disable\nusing MessagePack;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace MasterMemory.Tests.TestStructures\n{\n\n    [MemoryTable(\"people\"), MessagePackObject(true)]\n    public class PersonModel\n    {\n        [SecondaryKey(0), NonUnique]\n        [SecondaryKey(1, keyOrder: 1), NonUnique]\n        public string LastName { get; set; }\n\n        [SecondaryKey(2), NonUnique]\n        [SecondaryKey(1, keyOrder: 0), NonUnique]\n        public string FirstName { get; set; }\n\n        [PrimaryKey] public string RandomId { get; set; }\n    }\n}\n"
  },
  {
    "path": "tests/MasterMemory.Tests/TestStructures/QuestMaster.cs",
    "content": "﻿#pragma warning disable\nusing MessagePack;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace MasterMemory.Tests.TestStructures\n{\n    [MemoryTable(\"quest_master\"), MessagePackObject(true)]\n    public class QuestMaster : IValidatable<QuestMaster>\n    {\n        [PrimaryKey]\n        public int QuestId { get; set; }\n        public string Name { get; set; }\n        public int RewardItemId { get; set; }\n        public int Cost { get; set; }\n\n        public void Validate(IValidator<QuestMaster> validator)\n        {\n            var itemMaster = validator.GetReferenceSet<ItemMaster>();\n\n            itemMaster.Exists(x => x.RewardItemId, x => x.ItemId);\n\n            validator.Validate(x => x.Cost <= 100);\n            validator.Validate(x => x.Cost >= 0, \">= 0!!!\");\n\n            validator.ValidateAction(() => this.Cost <= 1000);\n            validator.ValidateAction(() => this.Cost >= -90, \">= -90!!!\");\n\n            if (validator.CallOnce())\n            {\n                var quests = validator.GetTableSet();\n                quests.Unique(x => x.Name);\n            }\n        }\n    }\n\n    [MemoryTable(\"item_master\"), MessagePackObject(true)]\n    public class ItemMaster : IValidatable<ItemMaster>\n    {\n        [PrimaryKey]\n        public int ItemId { get; set; }\n\n        public void Validate(IValidator<ItemMaster> validator)\n        {\n        }\n    }\n\n    [MemoryTable(\"quest_master_empty\"), MessagePackObject(true)]\n    public class QuestMasterEmptyValidate\n    {\n        [PrimaryKey]\n        public int QuestId { get; set; }\n        public string Name { get; set; }\n        public int RewardItemId { get; set; }\n        public int Cost { get; set; }\n    }\n\n    [MemoryTable(\"item_master_empty\"), MessagePackObject(true)]\n    public class ItemMasterEmptyValidate\n    {\n        [PrimaryKey]\n        public int ItemId { get; set; }\n    }\n\n    [MemoryTable(\"sequantial_master\"), MessagePackObject(true)]\n    public class SequentialCheckMaster : IValidatable<SequentialCheckMaster>\n    {\n        [PrimaryKey]\n        public int Id { get; set; }\n        public int Cost { get; set; }\n\n        public void Validate(IValidator<SequentialCheckMaster> validator)\n        {\n            if (validator.CallOnce())\n            {\n                var set = validator.GetTableSet();\n\n                set.Sequential(x => x.Id);\n                set.Sequential(x => x.Cost, true);\n            }\n        }\n    }\n\n    [MemoryTable(\"single_master\"), MessagePackObject(true)]\n    public class SingleMaster : IValidatable<SingleMaster>\n    {\n        public static int CalledValidateCount;\n        public static int CalledOnceCount;\n\n        [PrimaryKey]\n        public int Id { get; set; }\n\n        public void Validate(IValidator<SingleMaster> validator)\n        {\n            CalledValidateCount++;\n            if (validator.CallOnce())\n            {\n                CalledOnceCount++;\n            }\n        }\n    }\n\n    [MemoryTable(\"fail\"), MessagePackObject(true)]\n    public class Fail : IValidatable<Fail>\n    {\n        [PrimaryKey]\n        public int Id { get; set; }\n\n        public void Validate(IValidator<Fail> validator)\n        {\n            validator.Fail(\"Failed Id:\" + Id);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/MasterMemory.Tests/TestStructures/Sample.cs",
    "content": "﻿#pragma warning disable\nusing MessagePack;\n\nnamespace MasterMemory.Tests\n{\n    [MemoryTable(\"s_a_m_p_l_e\"), MessagePackObject(true)]\n    public class Sample\n    {\n        [PrimaryKey]\n        [SecondaryKey(1)]\n        [SecondaryKey(2)]\n        [SecondaryKey(3)]\n        public int Id { get; set; }\n        [SecondaryKey(1)]\n        [SecondaryKey(2)]\n        [SecondaryKey(3)]\n        [SecondaryKey(5), NonUnique]\n        [SecondaryKey(6, 1), NonUnique]\n        public int Age { get; set; }\n        [SecondaryKey(0)]\n        [SecondaryKey(1)]\n        [SecondaryKey(3)]\n        [SecondaryKey(4), NonUnique]\n        [SecondaryKey(6, 0), NonUnique]\n        public string FirstName { get; set; }\n        [SecondaryKey(0)]\n        [SecondaryKey(1)]\n        public string LastName { get; set; }\n\n\n        [MessagePack.IgnoreMember]\n        public int Hoge { get; set; }\n\n        [System.Runtime.Serialization.IgnoreDataMember]\n        public int Huga { get; set; }\n\n        public override string ToString()\n        {\n            return $\"{Id} {Age} {FirstName} {LastName}\";\n        }\n\n        public Sample()\n        {\n\n        }\n\n        public Sample(int Id, int Age, string FirstName, string LastName)\n        {\n            this.Id = Id;\n            this.Age = Age;\n            this.FirstName = FirstName;\n            this.LastName = LastName;\n        }\n\n\n    }\n}\n"
  },
  {
    "path": "tests/MasterMemory.Tests/TestStructures/SkillMaster.cs",
    "content": "﻿#pragma warning disable\nusing MessagePack;\n\nnamespace MasterMemory.Tests\n{\n    [MemoryTable(\"skillmaster\"), MessagePackObject(true)]\n    public class SkillMaster\n    {\n        [PrimaryKey]\n        public int SkillId { get; set; }\n        [PrimaryKey]\n        public int SkillLevel { get; set; }\n        public int AttackPower { get; set; }\n        public string SkillName { get; set; }\n        public string Description { get; set; }\n\n        public SkillMaster()\n        {\n\n        }\n\n        public SkillMaster(int SkillId, int SkillLevel, int AttackPower, string SkillName, string Description)\n        {\n            this.SkillId = SkillId;\n            this.SkillLevel = SkillLevel;\n            this.AttackPower = AttackPower;\n            this.SkillName = SkillName;\n            this.Description = Description;\n        }\n\n    }\n}\n"
  },
  {
    "path": "tests/MasterMemory.Tests/TestStructures/TestMaster.cs",
    "content": "using MessagePack;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace MasterMemory.Tests.TestStructures\n{\n    [MessagePackObject(true)]\n    [MemoryTable(nameof(TestMaster))]\n    public class TestMaster\n    {\n        [PrimaryKey, NonUnique]\n        public int TestID { get; set; }\n        public int Value { get; set; }\n\n        public TestMaster(int TestID, int Value)\n        {\n            this.TestID = TestID;\n            this.Value = Value;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/MasterMemory.Tests/TestStructures/UserLevel.cs",
    "content": "using MessagePack;\n\nnamespace MasterMemory.Tests\n{\n    [MemoryTable(\"UserLevel\"), MessagePackObject(true)]\n    public class UserLevel\n    {\n        [PrimaryKey]\n        public int Level { get; set; }\n        [SecondaryKey(0)]\n        public int Exp { get; set; }\n\n        public UserLevel()\n        {\n\n        }\n\n        public UserLevel(int Level, int Exp)\n        {\n            this.Level = Level;\n            this.Exp = Exp;\n        }\n\n    }\n}"
  },
  {
    "path": "tests/MasterMemory.Tests/ValidatorTest.cs",
    "content": "﻿using MasterMemory.Tests.TestStructures;\nusing MessagePack;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing Xunit;\nusing System.Linq;\n\nnamespace MasterMemory.Tests\n{\n    public class ValidatorTest\n    {\n        readonly Xunit.Abstractions.ITestOutputHelper output;\n\n#if UNITY_2018_3_OR_NEWER\n        public ValidatorTest()\n        {\n            this.output = new Xunit.Abstractions.DebugLogTestOutputHelper();\n            MessagePackSerializer.DefaultOptions = MessagePackSerializer.DefaultOptions.WithResolver(MessagePackResolver.Instance);\n        }\n#else\n        public ValidatorTest(Xunit.Abstractions.ITestOutputHelper output)\n        {\n            this.output = output;\n            MessagePackSerializer.DefaultOptions = MessagePackSerializer.DefaultOptions.WithResolver(MessagePackResolver.Instance);\n        }\n#endif\n\n        MemoryDatabase CreateDatabase(Fail[] data1)\n        {\n\n            var bin = new DatabaseBuilder()\n                .Append(data1)\n                .Build();\n\n            return new MemoryDatabase(bin, internString: false);\n        }\n\n        MemoryDatabase CreateDatabase(SingleMaster[] data1)\n        {\n\n            var bin = new DatabaseBuilder()\n                .Append(data1)\n                .Build();\n\n            return new MemoryDatabase(bin, internString: false);\n        }\n\n        MemoryDatabase CreateDatabase(SequentialCheckMaster[] data1)\n        {\n\n            var bin = new DatabaseBuilder()\n                .Append(data1)\n                .Build();\n\n            return new MemoryDatabase(bin, internString: false);\n        }\n\n        MemoryDatabase CreateDatabase(QuestMaster[] data1, ItemMaster[] data2)\n        {\n\n            var bin = new DatabaseBuilder()\n                .Append(data1)\n                .Append(data2)\n                .Build();\n\n            return new MemoryDatabase(bin, internString: false);\n        }\n\n        MemoryDatabase CreateDatabase(QuestMasterEmptyValidate[] data1, ItemMasterEmptyValidate[] data2)\n        {\n\n            var bin = new DatabaseBuilder()\n                .Append(data1)\n                .Append(data2)\n                .Build();\n\n            return new MemoryDatabase(bin, internString: false);\n        }\n\n        [Fact]\n        public void Empty()\n        {\n            var validateResult = CreateDatabase(new QuestMaster[]\n            {\n            }, new ItemMaster[]\n            {\n            }).Validate();\n\n            validateResult.IsValidationFailed.ShouldBeFalse();\n            validateResult.FailedResults.Count.ShouldBe(0);\n        }\n\n        [Fact]\n        public void PKUnique()\n        {\n            var validateResult = CreateDatabase(new QuestMasterEmptyValidate[]\n            {\n                new QuestMasterEmptyValidate { QuestId = 1 },\n                new QuestMasterEmptyValidate { QuestId = 2 },\n                new QuestMasterEmptyValidate { QuestId = 1 },\n                new QuestMasterEmptyValidate { QuestId = 4 },\n                new QuestMasterEmptyValidate { QuestId = 4 },\n            }, new ItemMasterEmptyValidate[]\n            {\n                new ItemMasterEmptyValidate { ItemId = 1 },\n                new ItemMasterEmptyValidate { ItemId = 2 },\n                new ItemMasterEmptyValidate { ItemId = 2 },\n            }).Validate();\n            output.WriteLine(validateResult.FormatFailedResults());\n\n            validateResult.IsValidationFailed.ShouldBeTrue();\n            validateResult.FailedResults.Count.ShouldBe(3); // Q:1,4 + I:2\n            var faileds = validateResult.FailedResults.OrderBy(x => x.Message).ToArray();\n\n            faileds[0].Message.ShouldBe(\"Unique failed: ItemId, value = 2\");\n            faileds[1].Message.ShouldBe(\"Unique failed: QuestId, value = 1\");\n            faileds[2].Message.ShouldBe(\"Unique failed: QuestId, value = 4\");\n        }\n\n        // test IValidator\n\n        /*\n        public interface IValidator<T>\n        {\n            ValidatableSet<T> GetTableSet();\n            ReferenceSet<T, TRef> GetReferenceSet<TRef>();\n            void Validate(Expression<Func<T, bool>> predicate);\n            void Validate(Func<T, bool> predicate, string message);\n            void ValidateAction(Expression<Func<bool>> predicate);\n            void ValidateAction(Func<bool> predicate, string message);\n            void Fail(string message);\n            bool CallOnce();\n        }\n\n        ReferenceSet.Exists\n        ValidatableSet.Unique\n        ValidatableSet.Sequential\n    */\n\n        [Fact]\n        public void Exists()\n        {\n            var validateResult = CreateDatabase(new QuestMaster[]\n            {\n                new QuestMaster { QuestId = 1, RewardItemId = 1, Name = \"foo\" },\n                new QuestMaster { QuestId = 2, RewardItemId = 3, Name = \"bar\" },\n                new QuestMaster { QuestId = 3, RewardItemId = 2, Name = \"baz\" },\n                new QuestMaster { QuestId = 4, RewardItemId = 5, Name = \"tako\"},\n                new QuestMaster { QuestId = 5, RewardItemId = 4, Name = \"nano\"},\n            }, new ItemMaster[]\n            {\n                new ItemMaster { ItemId = 1 },\n                new ItemMaster { ItemId = 2 },\n                new ItemMaster { ItemId = 3 },\n            }).Validate();\n            output.WriteLine(validateResult.FormatFailedResults());\n            validateResult.IsValidationFailed.ShouldBeTrue();\n\n            validateResult.FailedResults[0].Message.ShouldBe(\"Exists failed: QuestMaster.RewardItemId -> ItemMaster.ItemId, value = 5, PK(QuestId) = 4\");\n            validateResult.FailedResults[1].Message.ShouldBe(\"Exists failed: QuestMaster.RewardItemId -> ItemMaster.ItemId, value = 4, PK(QuestId) = 5\");\n        }\n\n        [Fact]\n        public void Unique()\n        {\n            var validateResult = CreateDatabase(new QuestMaster[]\n            {\n                new QuestMaster { QuestId = 1, Name = \"foo\" },\n                new QuestMaster { QuestId = 2, Name = \"bar\" },\n                new QuestMaster { QuestId = 3, Name = \"bar\" },\n                new QuestMaster { QuestId = 4, Name = \"tako\" },\n                new QuestMaster { QuestId = 5, Name = \"foo\" },\n            }, new ItemMaster[]\n            {\n                new ItemMaster { ItemId = 0 }\n            }).Validate();\n            output.WriteLine(validateResult.FormatFailedResults());\n            validateResult.IsValidationFailed.ShouldBeTrue();\n\n            validateResult.FailedResults[0].Message.ShouldBe(\"Unique failed: .Name, value = bar, PK(QuestId) = 3\");\n            validateResult.FailedResults[1].Message.ShouldBe(\"Unique failed: .Name, value = foo, PK(QuestId) = 5\");\n        }\n\n        [Fact]\n        public void Sequential()\n        {\n            {\n                var validateResult = CreateDatabase(new SequentialCheckMaster[]\n                {\n                    new SequentialCheckMaster { Id = 1, Cost = 10 },\n                    new SequentialCheckMaster { Id = 2, Cost = 11 },\n                    new SequentialCheckMaster { Id = 3, Cost = 11 },\n                    new SequentialCheckMaster { Id = 4, Cost = 12 },\n                }).Validate();\n                output.WriteLine(validateResult.FormatFailedResults());\n                validateResult.IsValidationFailed.ShouldBeFalse();\n            }\n            {\n                var validateResult = CreateDatabase(new SequentialCheckMaster[]\n                {\n                    new SequentialCheckMaster { Id = 1, Cost = 10 },\n                    new SequentialCheckMaster { Id = 2, Cost = 11 },\n                    new SequentialCheckMaster { Id = 3, Cost = 11 },\n                    new SequentialCheckMaster { Id = 5, Cost = 13 },\n                }).Validate();\n                output.WriteLine(validateResult.FormatFailedResults());\n                validateResult.IsValidationFailed.ShouldBeTrue();\n\n                validateResult.FailedResults[0].Message.ShouldBe(\"Sequential failed: .Id, value = (3, 5), PK(Id) = 5\");\n                validateResult.FailedResults[1].Message.ShouldBe(\"Sequential failed: .Cost, value = (11, 13), PK(Id) = 5\");\n            }\n        }\n\n        [Fact]\n        public void CallOnce()\n        {\n            _ = CreateDatabase(new SingleMaster[]\n            {\n                new SingleMaster { Id = 1},\n                new SingleMaster { Id = 2},\n                new SingleMaster { Id = 3},\n                new SingleMaster { Id = 4},\n            }).Validate();\n\n\n            SingleMaster.CalledValidateCount.ShouldBe(4);\n            SingleMaster.CalledOnceCount.ShouldBe(1);\n        }\n\n        [Fact]\n        public void Validate()\n        {\n            var validateResult = CreateDatabase(new QuestMaster[]\n            {\n                new QuestMaster { QuestId = 1, RewardItemId = 1, Name = \"foo\", Cost = -1 },\n                new QuestMaster { QuestId = 2, RewardItemId = 3, Name = \"bar\", Cost = 99 },\n                new QuestMaster { QuestId = 3, RewardItemId = 2, Name = \"baz\", Cost = 100 },\n                new QuestMaster { QuestId = 4, RewardItemId = 3, Name = \"tao\", Cost = 101 },\n                new QuestMaster { QuestId = 5, RewardItemId = 3, Name = \"nao\", Cost = 33 },\n            }, new ItemMaster[]\n            {\n                new ItemMaster { ItemId = 1 },\n                new ItemMaster { ItemId = 2 },\n                new ItemMaster { ItemId = 3 },\n            }).Validate();\n            output.WriteLine(validateResult.FormatFailedResults());\n            validateResult.IsValidationFailed.ShouldBeTrue();\n\n            validateResult.FailedResults[0].Message.ShouldBe(\"Validate failed: >= 0!!!, PK(QuestId) = 1\");\n            validateResult.FailedResults[1].Message.ShouldBe(\"Validate failed: (this.Cost <= 100), Cost = 101, PK(QuestId) = 4\");\n        }\n\n        [Fact]\n        public void ValidateAction()\n        {\n            var validateResult = CreateDatabase(new QuestMaster[]\n             {\n                new QuestMaster { QuestId = 1, RewardItemId = 1, Name = \"foo\", Cost = -100 },\n                new QuestMaster { QuestId = 2, RewardItemId = 3, Name = \"bar\", Cost = 99 },\n                new QuestMaster { QuestId = 3, RewardItemId = 2, Name = \"baz\", Cost = 100 },\n                new QuestMaster { QuestId = 4, RewardItemId = 3, Name = \"tao\", Cost = 1001 },\n                new QuestMaster { QuestId = 5, RewardItemId = 3, Name = \"nao\", Cost = 33 },\n             }, new ItemMaster[]\n             {\n                new ItemMaster { ItemId = 1 },\n                new ItemMaster { ItemId = 2 },\n                new ItemMaster { ItemId = 3 },\n             }).Validate();\n            output.WriteLine(validateResult.FormatFailedResults());\n            validateResult.IsValidationFailed.ShouldBeTrue();\n\n            var results = validateResult.FailedResults.Select(x => x.Message).Where(x => x.Contains(\"ValidateAction faile\")).ToArray();\n\n            results[0].ShouldBe(\"ValidateAction failed: >= -90!!!, PK(QuestId) = 1\");\n            results[1].ShouldBe(\"ValidateAction failed: (value(MasterMemory.Tests.TestStructures.QuestMaster).Cost <= 1000), PK(QuestId) = 4\");\n        }\n\n        [Fact]\n        public void Fail()\n        {\n            var validateResult = CreateDatabase(new Fail[]\n            {\n                new Fail { Id = 1},\n                new Fail { Id = 2},\n                new Fail { Id = 3},\n            }).Validate();\n            output.WriteLine(validateResult.FormatFailedResults());\n            validateResult.IsValidationFailed.ShouldBeTrue();\n\n            var msg = validateResult.FailedResults.Select(x => x.Message).ToArray();\n            msg[0].ShouldBe(\"Failed Id:1, PK(Id) = 1\");\n            msg[1].ShouldBe(\"Failed Id:2, PK(Id) = 2\");\n            msg[2].ShouldBe(\"Failed Id:3, PK(Id) = 3\");\n        }\n    }\n}\n"
  }
]