[
  {
    "path": ".gitignore",
    "content": "#ignore thumbnails created by windows\nThumbs.db\n#Ignore files build by Visual Studio\n*.user\n*.aps\n*.pch\n*.vspscc\n*_i.c\n*_p.c\n*.ncb\n*.suo\n*.bak\n*.cache\n*.ilk\n*.log\n[Bb]in\n[Dd]ebug*/\n*.sbr\nobj/\n[Rr]elease*/\n_ReSharper*/\nTestResults*/\n*.nupkg\n/.vs/SharedMemory\n/nuget.exe\n/.vs/ProjectSettings.json\n/.vs/slnx.sqlite\n/.vs/VSWorkspaceState.json\n\n# JetBrains Rider\n.idea/\n*.sln.iml\n"
  },
  {
    "path": "Examples/ClientTest/ClientTest.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFrameworks>netcoreapp3.0;netcoreapp2.0;net47;net46;net45;net4;net35</TargetFrameworks>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\SharedMemory\\SharedMemory.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Examples/ClientTest/Program.cs",
    "content": "﻿// SharedMemory (File: ClientTest\\Program.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing SharedMemory;\n\n#if NET40Plus\nusing System.Threading.Tasks;\n#endif\n\nnamespace ClientTest\n{\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            Console.WriteLine(\"Press <enter> to start client\");\n\n            Console.ReadLine();\n\n            Console.WriteLine(\"Open existing shared memory circular buffer\");\n            using (SharedMemory.CircularBuffer theClient = new SharedMemory.CircularBuffer(\"TEST\"))\n            {\n                Console.WriteLine(\"Buffer {0} opened, NodeBufferSize: {1}, NodeCount: {2}\", theClient.Name, theClient.NodeBufferSize, theClient.NodeCount);\n\n                long bufferSize = theClient.NodeBufferSize;\n                byte[] writeDataProof;\n                byte[] writeData = new byte[bufferSize];\n\n                List<byte[]> dataList = new List<byte[]>();\n\n                // Generate data for integrity check\n                for (var j = 0; j < 256; j++)\n                {\n                    var data = new byte[bufferSize];\n                    for (var i = 0; i < data.Length; i++)\n                    {\n                        data[i] = (byte)((i + j) % 255);\n                    }\n                    dataList.Add(data);\n                }\n\n                int skipCount = 0;\n                long iterations = 0;\n                long totalBytes = 0;\n                long lastTick = 0;\n                Stopwatch sw = Stopwatch.StartNew();\n\n                int threadCount = 0;\n                Action reader = () =>\n                {\n                    int myThreadIndex = Interlocked.Increment(ref threadCount);\n                    int linesOut = 0;\n                    bool finalLine = false;\n                    for (; ; )\n                    {\n                        int amount = theClient.Read(writeData, 100);\n                        //int amount = theClient.Read<byte>(writeData, 100);\n\n                        if (amount == 0)\n                        {\n                            Interlocked.Increment(ref skipCount);\n                        }\n                        else\n                        {\n                            // Only check data integrity for first thread\n                            if (threadCount == 1)\n                            {\n                                bool mismatch = false;\n\n                                writeDataProof = dataList[((int)Interlocked.Read(ref iterations)) % 255];\n                                for (var i = 0; i < writeDataProof.Length; i++)\n                                {\n                                    if (writeData[i] != writeDataProof[i])\n                                    {\n                                        mismatch = true;\n                                        Console.WriteLine(\"Buffers don't match!\");\n                                        break;\n                                    }\n                                }\n\n                                if (mismatch)\n                                    break;\n                            }\n\n                            Interlocked.Add(ref totalBytes, amount);\n\n                            Interlocked.Increment(ref iterations);\n                        }\n\n                        if (threadCount == 1 && Interlocked.Read(ref iterations) > 500)\n                            finalLine = true;\n\n                        if (myThreadIndex < 3 && (finalLine || sw.ElapsedTicks - lastTick > 1000000))\n                        {\n                            lastTick = sw.ElapsedTicks;\n                            Console.WriteLine(\"Read: {0}, Wait: {1}, {2}MB/s\", ((double)totalBytes / 1048576.0).ToString(\"F0\"), skipCount, (((totalBytes / 1048576.0) / sw.ElapsedMilliseconds) * 1000).ToString(\"F2\"));\n                            linesOut++;\n                            if (finalLine || (myThreadIndex > 1 && linesOut > 10))\n                            {\n                                Console.WriteLine(\"Completed.\");\n                                break;\n                            }\n                        }\n                    }\n                };\n\n                Console.WriteLine(\"Testing data integrity (high CPU, low bandwidth)...\");\n                reader();\n                Console.WriteLine(\"\");\n\n                skipCount = 0;\n                iterations = 0;\n                totalBytes = 0;\n                lastTick = 0;\n                sw.Reset();\n                sw.Start();\n                Console.WriteLine(\"Testing data throughput (low CPU, high bandwidth)...\");\n#if NET40Plus                \n                Task c1 = Task.Factory.StartNew(reader);\n                Task c2 = Task.Factory.StartNew(reader);\n                Task c3 = Task.Factory.StartNew(reader);\n                //Task c4 = Task.Factory.StartNew(reader);\n#else\n                ThreadPool.QueueUserWorkItem((o) => { reader(); });\n                ThreadPool.QueueUserWorkItem((o) => { reader(); });\n                ThreadPool.QueueUserWorkItem((o) => { reader(); });\n#endif\n                Console.ReadLine();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/RpcTest/Program.cs",
    "content": "﻿// SharedMemory (File: RpcTest\\Program.cs)\n// Copyright (c) 2020 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nusing SharedMemory;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace RpcTest\n{\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            long completed = 0;\n            long count = 0;\n            byte[][] dataList;\n            int loopCount = 2000;\n            int bufSize = 1024 * 500;\n            int bufferCapacity = bufSize + 64; // buf size + enough room for protocol header\n            int threadCount = 1;\n            int dataListCount = 256;\n            \n            // Generate random data to be written\n            Random random = new Random();\n            dataList = new byte[dataListCount][];\n            for (var j = 0; j < dataListCount; j++)\n            {\n                var data = new byte[bufSize];\n                random.NextBytes(data);\n                dataList[j] = data;\n            }\n\n            Console.WriteLine($\"Thread count: {threadCount}\");\n            Console.WriteLine($\"Buffer size: {bufferCapacity}\");\n            Console.WriteLine($\"Message size: {bufSize}\");\n\n            Console.WriteLine(\"Running...\");\n\n            Stopwatch watch = Stopwatch.StartNew();\n\n            for (var i = 0; i < threadCount; i++)\n            {\n                new Task(async () =>\n                    {\n                        RpcBuffer ipcMaster = null;\n                        RpcBuffer ipcSlave = null;\n                        var name = $\"MasterSlaveTest{Guid.NewGuid()}\";\n                        ipcMaster = new RpcBuffer(name, bufferCapacity: bufferCapacity);\n                        ipcSlave = new RpcBuffer(name, (msgId, payload) =>\n                        {\n                            Interlocked.Increment(ref count);\n                            return (byte[])null;\n                            //return new byte[] { (byte)(payload[0] * payload[1]) };\n                        });\n                        var rnd = new Random();\n                        var watchLine = Stopwatch.StartNew();\n                        for (var j = 0; j < loopCount; j++)\n                        {\n                            var result = await ipcMaster.RemoteRequestAsync(dataList[rnd.Next(0, dataList.Length)]);\n                            if (!result.Success)\n                            {\n                                Console.WriteLine(\"Failed\");\n                                return;\n                            }\n                        }\n                        Interlocked.Increment(ref completed);\n                    }).Start();\n            }\n\n            while(Interlocked.Read(ref completed) < threadCount)\n            {\n                Thread.Sleep(0);\n            }\n\n            watch.Stop();\n            Console.WriteLine($\"{count} in {watch.Elapsed}, {(int)(count / watch.Elapsed.TotalSeconds)} requests / sec\");\n\n            Console.ReadLine();\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/RpcTest/RpcTest.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFrameworks>netcoreapp3.0;netcoreapp2.0;net47;net46;net45</TargetFrameworks>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\SharedMemory\\SharedMemory.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <Folder Include=\"Properties\\\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "Examples/ServerTest/Program.cs",
    "content": "﻿// SharedMemory (File: ServerTest\\Program.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\n#if NET40Plus\nusing System.Threading.Tasks;\n#endif\n\nnamespace ServerTest\n{\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            int bufferSize = 1048576;\n            byte[] readData;\n            int size = sizeof(byte) * bufferSize;\n            int count = 50;\n\n            // Generate data to be written\n            byte[][] dataList = new byte[256][];\n            for (var j = 0; j < 256; j++)\n            {\n                var data = new byte[bufferSize];\n                for (var i = 0; i < data.Length; i++)\n                {\n                    data[i] = (byte)((i + j) % 255);\n                }\n                dataList[j] = data;\n            }\n\n            Console.WriteLine(\"Press <enter> to start Server\");\n            Console.ReadLine();\n\n\n            Console.WriteLine(\"Create shared memory circular buffer\");\n            using (var theServer = new SharedMemory.CircularBuffer(\"TEST\", count, size))\n            {\n                Console.WriteLine(\"Circular buffer producer created.\");\n                Console.WriteLine(\"Ready for client...\");\n                Thread.Sleep(1000);\n\n                int skipCount = 0;\n                long iterations = 0;\n                long totalBytes = 0;\n                long lastTick = 0;\n                Stopwatch sw = Stopwatch.StartNew();                \n                int threadCount = 0;\n                Action writer = () =>\n                {\n                    int myThreadIndex = Interlocked.Increment(ref threadCount);\n                    int linesOut = 0;\n                    bool finalLine = false;\n                    for (; ; )\n                    {\n                        readData = dataList[iterations % 255];\n\n                        int amount = theServer.Write(readData, 100);\n                        //int amount = theServer.Write<byte>(readData, 100);\n\n                        if (amount == 0)\n                        {\n                            Interlocked.Increment(ref skipCount);\n                        }\n                        else\n                        {\n                            Interlocked.Add(ref totalBytes, amount);\n                            Interlocked.Increment(ref iterations);\n                        }\n\n                        if (threadCount == 1 && Interlocked.Read(ref iterations) > 500)\n                            finalLine = true;\n\n                        if (myThreadIndex < 3 && (finalLine || sw.ElapsedTicks - lastTick > 1000000))\n                        {\n                            lastTick = sw.ElapsedTicks;\n                            Console.WriteLine(\"Write: {0}, Wait: {1}, {2}MB/s\", ((double)totalBytes / 1048576.0).ToString(\"F0\"), skipCount, (((totalBytes / 1048576.0) / sw.ElapsedMilliseconds) * 1000).ToString(\"F2\"));\n                            linesOut++;\n                            if (finalLine || (myThreadIndex > 1 && linesOut > 10))\n                            {\n                                Console.WriteLine(\"Completed.\");\n                                break;\n                            }\n                        }\n                    }\n                };\n\n                writer();\n                Console.WriteLine(\"\");\n                skipCount = 0;\n                iterations = 0;\n                totalBytes = 0;\n                lastTick = 0;\n                sw.Reset();\n                sw.Start();\n\n                Console.WriteLine(\"Testing throughput...\");\n#if NET40Plus\n                Task s1 = Task.Factory.StartNew(writer);\n                //Task s2 = Task.Factory.StartNew(writer);\n                //Task s3 = Task.Factory.StartNew(writer);\n#else\n                ThreadPool.QueueUserWorkItem((o) =>\n                {\n                    writer();\n                });\n#endif\n                Console.ReadLine();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/ServerTest/ServerTest.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFrameworks>netcoreapp3.0;netcoreapp2.0;net47;net46;net45;net4;net35</TargetFrameworks>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\SharedMemory\\SharedMemory.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "Examples/SingleProcess/CommandLineParser.cs",
    "content": "﻿//////////////////////////////////////////////////////////////////////////////\n//    Command Line Argument Parser\n//    ----------------------------\n//\n//    Author: hotweird@hotmail.com\n//\n//    Microsoft Public License (Ms-PL)\n//\n//    This license governs use of the accompanying software. If you use the software, you\n//    accept this license. If you do not accept the license, do not use the software.\n//\n//    1. Definitions\n//\n//    The terms \"reproduce,\" \"reproduction,\" \"derivative works,\" and \"distribution\" have the\n//    same meaning here as under U.S. copyright law.\n//\n//    A \"contribution\" is the original software, or any additions or changes to the software.\n//\n//    A \"contributor\" is any person that distributes its contribution under this license.\n//\n//    \"Licensed patents\" are a contributor's patent claims that read directly on its contribution.\n//\n//    2. Grant of Rights\n//\n//    (A) Copyright Grant- Subject to the terms of this license, including the license conditions\n//        and limitations in section 3, each contributor grants you a non-exclusive, worldwide,\n//        royalty-free copyright license to reproduce its contribution, prepare derivative works\n//        of its contribution, and distribute its contribution or any derivative works that you create.\n//\n//    (B) Patent Grant- Subject to the terms of this license, including the license conditions and\n//        limitations in section 3, each contributor grants you a non-exclusive, worldwide,\n//        royalty-free license under its licensed patents to make, have made, use, sell, offer for\n//        sale, import, and/or otherwise dispose of its contribution in the software or derivative\n//        works of the contribution in the software.\n//\n//    3. Conditions and Limitations\n//\n//    (A) No Trademark License- This license does not grant you rights to use any contributors'\n//        name, logo, or trademarks.\n//\n//    (B) If you bring a patent claim against any contributor over patents that you claim are\n//        infringed by the software, your patent license from such contributor to the software ends\n//        automatically.\n//\n//    (C) If you distribute any portion of the software, you must retain all copyright, patent,\n//        trademark, and attribution notices that are present in the software.\n//\n//    (D) If you distribute any portion of the software in source code form, you may do so only under\n//        this license by including a complete copy of this license with your distribution. If you\n//        distribute any portion of the software in compiled or object code form, you may only do so\n//        under a license that complies with this license.\n//\n//    (E) The software is licensed \"as-is.\" You bear the risk of using it. The contributors give no\n//        express warranties, guarantees or conditions. You may have additional consumer rights under\n//        your local laws which this license cannot change. To the extent permitted under your local\n//        laws, the contributors exclude the implied warranties of merchantability, fitness for a\n//        particular purpose and non-infringement.\n//\n//    Usage\n//    -----\n//\n//    Parsing command line arguments to a console application is a common problem. \n//    This library handles the common task of reading arguments from a command line \n//    and filling in the values in a type.\n//\n//    To use this library, define a class whose fields represent the data that your \n//    application wants to receive from arguments on the command line. Then call \n//    CommandLine.ParseArguments() to fill the object with the data \n//    from the command line. Each field in the class defines a command line argument. \n//    The type of the field is used to validate the data read from the command line. \n//    The name of the field defines the name of the command line option.\n//\n//    The parser can handle fields of the following types:\n//\n//    - string\n//    - int\n//    - uint\n//    - bool\n//    - enum\n//    - array of the above type\n//\n//    For example, suppose you want to read in the argument list for wc (word count). \n//    wc takes three optional boolean arguments: -l, -w, and -c and a list of files.\n//\n//    You could parse these arguments using the following code:\n//\n//    class WCArguments\n//    {\n//        public bool lines;\n//        public bool words;\n//        public bool chars;\n//        public string[] files;\n//    }\n//\n//    class WC\n//    {\n//        static void Main(string[] args)\n//        {\n//            if (CommandLine.ParseArgumentsWithUsage(args, parsedArgs))\n//            {\n//            //     insert application code here\n//            }\n//        }\n//    }\n//\n//    So you could call this aplication with the following command line to count \n//    lines in the foo and bar files:\n//\n//        wc.exe /lines /files:foo /files:bar\n//\n//    The program will display the following usage message when bad command line \n//    arguments are used:\n//\n//        wc.exe -x\n//\n//    Unrecognized command line argument '-x'\n//        /lines[+|-]                         short form /l\n//        /words[+|-]                         short form /w\n//        /chars[+|-]                         short form /c\n//        /files:<string>                     short form /f\n//        @<file>                             Read response file for more options\n//\n//    That was pretty easy. However, you realy want to omit the \"/files:\" for the \n//    list of files. The details of field parsing can be controled using custom \n//    attributes. The attributes which control parsing behaviour are:\n//\n//    ArgumentAttribute \n//        - controls short name, long name, required, allow duplicates, default value\n//        and help text\n//    DefaultArgumentAttribute \n//        - allows omition of the \"/name\".\n//        - This attribute is allowed on only one field in the argument class.\n//\n//    So for the wc.exe program we want this:\n//\n//    using System;\n//    using Utilities;\n//\n//    class WCArguments\n//    {\n//        [Argument(ArgumentType.AtMostOnce, HelpText=\"Count number of lines in the input text.\")]\n//        public bool lines;\n//        [Argument(ArgumentType.AtMostOnce, HelpText=\"Count number of words in the input text.\")]\n//        public bool words;\n//        [Argument(ArgumentType.AtMostOnce, HelpText=\"Count number of chars in the input text.\")]\n//        public bool chars;\n//        [DefaultArgument(ArgumentType.MultipleUnique, HelpText=\"Input files to count.\")]\n//        public string[] files;\n//    }\n//\n//    class WC\n//    {\n//        static void Main(string[] args)\n//        {\n//            WCArguments parsedArgs = new WCArguments();\n//            if (CommandLine.ParseArgumentsWithUsage(args, parsedArgs))\n//            {\n//            //     insert application code here\n//            }\n//        }\n//    }\n//\n//\n//\n//    So now we have the command line we want:\n//\n//        wc.exe /lines foo bar\n//\n//    This will set lines to true and will set files to an array containing the \n//    strings \"foo\" and \"bar\".\n//\n//    The new usage message becomes:\n//\n//        wc.exe -x\n//\n//    Unrecognized command line argument '-x'\n//    /lines[+|-]  Count number of lines in the input text. (short form /l)\n//    /words[+|-]  Count number of words in the input text. (short form /w)\n//    /chars[+|-]  Count number of chars in the input text. (short form /c)\n//    @<file>      Read response file for more options\n//    <files>      Input files to count. (short form /f)\n//\n//    If you want more control over how error messages are reported, how /help is \n//    dealt with, etc you can instantiate the CommandLine.Parser class.\n//\n//\n//\n//    Cheers,\n//    Peter Hallam\n//    C# Compiler Developer\n//    Microsoft Corp.\n//\n//\n//\n//\n//    Release Notes\n//    -------------\n//\n//    10/02/2002 Initial Release\n//    10/14/2002 Bug Fix\n//    01/08/2003 Bug Fix in @ include files\n//    10/23/2004 Added user specified help text, formatting of help text to \n//            screen width. Added ParseHelp for /?.\n//    11/23/2004 Added support for default values.\n//    02/23/2005 Fix bug with short name and default arguments.\n//////////////////////////////////////////////////////////////////////////////\nnamespace CommandLine\n{\n    using System;\n    using System.Diagnostics;\n    using System.Reflection;\n    using System.Collections;\n    using System.IO;\n    using System.Text;\n    using System.Runtime.InteropServices;\n\n    /// <summary>\n    /// Used to control parsing of command line arguments.\n    /// </summary>\n    [Flags]    \n    public enum ArgumentType\n    {\n        /// <summary>\n        /// Indicates that this field is required. An error will be displayed\n        /// if it is not present when parsing arguments.\n        /// </summary>\n        Required    = 0x01,\n        /// <summary>\n        /// Only valid in conjunction with Multiple.\n        /// Duplicate values will result in an error.\n        /// </summary>\n        Unique      = 0x02,\n        /// <summary>\n        /// Inidicates that the argument may be specified more than once.\n        /// Only valid if the argument is a collection\n        /// </summary>\n        Multiple    = 0x04,\n\n        /// <summary>\n        /// The default type for non-collection arguments.\n        /// The argument is not required, but an error will be reported if it is specified more than once.\n        /// </summary>\n        AtMostOnce  = 0x00,\n        \n        /// <summary>\n        /// For non-collection arguments, when the argument is specified more than\n        /// once no error is reported and the value of the argument is the last\n        /// value which occurs in the argument list.\n        /// </summary>\n        LastOccurenceWins = Multiple,\n\n        /// <summary>\n        /// The default type for collection arguments.\n        /// The argument is permitted to occur multiple times, but duplicate \n        /// values will cause an error to be reported.\n        /// </summary>\n        MultipleUnique  = Multiple | Unique,\n    }\n    \n    /// <summary>\n    /// Allows control of command line parsing.\n    /// Attach this attribute to instance fields of types used\n    /// as the destination of command line argument parsing.\n    /// </summary>\n    [AttributeUsage(AttributeTargets.Field)]\n    public class ArgumentAttribute : Attribute\n    {\n        /// <summary>\n        /// Allows control of command line parsing.\n        /// </summary>\n        /// <param name=\"type\"> Specifies the error checking to be done on the argument. </param>\n        public ArgumentAttribute(ArgumentType type)\n        {\n            this.type = type;\n        }\n        \n        /// <summary>\n        /// The error checking to be done on the argument.\n        /// </summary>\n        public ArgumentType Type\n        {\n            get { return this.type; }\n        }\n        /// <summary>\n        /// Returns true if the argument did not have an explicit short name specified.\n        /// </summary>\n        public bool DefaultShortName    { get { return null == this.shortName; } }\n        \n        /// <summary>\n        /// The short name of the argument.\n        /// Set to null means use the default short name if it does not\n        /// conflict with any other parameter name.\n        /// Set to String.Empty for no short name.\n        /// This property should not be set for DefaultArgumentAttributes.\n        /// </summary>\n        public string ShortName\n        {\n            get { return this.shortName; }\n            set { Debug.Assert(value == null || !(this is DefaultArgumentAttribute)); this.shortName = value; }\n        }\n\n        /// <summary>\n        /// Returns true if the argument did not have an explicit long name specified.\n        /// </summary>\n        public bool DefaultLongName     { get { return null == this.longName; } }\n        \n        /// <summary>\n        /// The long name of the argument.\n        /// Set to null means use the default long name.\n        /// The long name for every argument must be unique.\n        /// It is an error to specify a long name of String.Empty.\n        /// </summary>\n        public string LongName\n        {\n            get { Debug.Assert(!this.DefaultLongName); return this.longName; }\n            set { Debug.Assert(value != \"\"); this.longName = value; }\n        }\n\n        /// <summary>\n        /// The default value of the argument.\n        /// </summary>\n        public object DefaultValue\n        {\n            get { return this.defaultValue; }\n            set { this.defaultValue = value; }\n        }\n        \n        /// <summary>\n        /// Returns true if the argument has a default value.\n        /// </summary>\n        public bool HasDefaultValue     { get { return null != this.defaultValue; } }\n\n        /// <summary>\n        /// Returns true if the argument has help text specified.\n        /// </summary>\n        public bool HasHelpText         { get { return null != this.helpText; } }\n        \n        /// <summary>\n        /// The help text for the argument.\n        /// </summary>\n        public string HelpText\n        {\n            get { return this.helpText; }\n            set { this.helpText = value; }\n        }\n        \n        private string shortName;\n        private string longName;\n        private string helpText;\n        private object defaultValue;\n        private ArgumentType type;\n    }\n\n    /// <summary>\n    /// Indicates that this argument is the default argument.\n    /// '/' or '-' prefix only the argument value is specified.\n    /// The ShortName property should not be set for DefaultArgumentAttribute\n    /// instances. The LongName property is used for usage text only and\n    /// does not affect the usage of the argument.\n    /// </summary>\n    [AttributeUsage(AttributeTargets.Field)]\n    public class DefaultArgumentAttribute : ArgumentAttribute\n    {\n        /// <summary>\n        /// Indicates that this argument is the default argument.\n        /// </summary>\n        /// <param name=\"type\"> Specifies the error checking to be done on the argument. </param>\n        public DefaultArgumentAttribute(ArgumentType type)\n            : base (type)\n        {\n        }\n    }\n\n    /// <summary>\n    /// A delegate used in error reporting.\n    /// </summary>\n    public delegate void ErrorReporter(string message);\n\n    /// <summary>\n    /// Parser for command line arguments.\n    ///\n    /// The parser specification is infered from the instance fields of the object\n    /// specified as the destination of the parse.\n    /// Valid argument types are: int, uint, string, bool, enums\n    /// Also argument types of Array of the above types are also valid.\n    /// \n    /// Error checking options can be controlled by adding a ArgumentAttribute\n    /// to the instance fields of the destination object.\n    ///\n    /// At most one field may be marked with the DefaultArgumentAttribute\n    /// indicating that arguments without a '-' or '/' prefix will be parsed as that argument.\n    ///\n    /// If not specified then the parser will infer default options for parsing each\n    /// instance field. The default long name of the argument is the field name. The\n    /// default short name is the first character of the long name. Long names and explicitly\n    /// specified short names must be unique. Default short names will be used provided that\n    /// the default short name does not conflict with a long name or an explicitly\n    /// specified short name.\n    ///\n    /// Arguments which are array types are collection arguments. Collection\n    /// arguments can be specified multiple times.\n    /// </summary>\n    public sealed class Parser\n    {\n        /// <summary>\n        /// The System Defined new line string.\n        /// </summary>\n        public const string NewLine = \"\\r\\n\";\n        \n        /// <summary>\n        /// Don't ever call this.\n        /// </summary>\n        private Parser() { }\n        \n        /// <summary>\n        /// Parses Command Line Arguments. Displays usage message to Console.Out\n        /// if /?, /help or invalid arguments are encounterd.\n        /// Errors are output on Console.Error.\n        /// Use ArgumentAttributes to control parsing behaviour.\n        /// </summary>\n        /// <param name=\"arguments\"> The actual arguments. </param>\n        /// <param name=\"destination\"> The resulting parsed arguments. </param>\n        /// <returns> true if no errors were detected. </returns>\n        public static bool ParseArgumentsWithUsage(string [] arguments, object destination)\n        {\n            if (Parser.ParseHelp(arguments) || !Parser.ParseArguments(arguments, destination))\n            {\n                // error encountered in arguments. Display usage message\n                System.Console.Write(Parser.ArgumentsUsage(destination.GetType()));\n                return false;\n            }\n            \n            return true;\n        }\n\n        /// <summary>\n        /// Parses Command Line Arguments. \n        /// Errors are output on Console.Error.\n        /// Use ArgumentAttributes to control parsing behaviour.\n        /// </summary>\n        /// <param name=\"arguments\"> The actual arguments. </param>\n        /// <param name=\"destination\"> The resulting parsed arguments. </param>\n        /// <returns> true if no errors were detected. </returns>\n        public static bool ParseArguments(string [] arguments, object destination)\n        {\n            return Parser.ParseArguments(arguments, destination, new ErrorReporter(Console.Error.WriteLine));\n        }\n        \n        /// <summary>\n        /// Parses Command Line Arguments. \n        /// Use ArgumentAttributes to control parsing behaviour.\n        /// </summary>\n        /// <param name=\"arguments\"> The actual arguments. </param>\n        /// <param name=\"destination\"> The resulting parsed arguments. </param>\n        /// <param name=\"reporter\"> The destination for parse errors. </param>\n        /// <returns> true if no errors were detected. </returns>\n        public static bool ParseArguments(string[] arguments, object destination, ErrorReporter reporter)\n        {\n            Parser parser = new Parser(destination.GetType(), reporter);\n            return parser.Parse(arguments, destination);\n        }\n\n        private static void NullErrorReporter(string message) \n        { \n        } \n\n        private class HelpArgument \n        { \n            [ArgumentAttribute(ArgumentType.AtMostOnce, ShortName=\"?\")] \n            public bool help = false; \n        } \n\n        /// <summary>\n        /// Checks if a set of arguments asks for help.\n        /// </summary>\n        /// <param name=\"args\"> Args to check for help. </param>\n        /// <returns> Returns true if args contains /? or /help. </returns>\n        public static bool ParseHelp(string[] args)\n        {\n            Parser helpParser = new Parser(typeof(HelpArgument), new ErrorReporter(NullErrorReporter));\n            HelpArgument helpArgument = new HelpArgument();\n            helpParser.Parse(args, helpArgument);\n            return helpArgument.help;\n        }\n\n\n        /// <summary>\n        /// Returns a Usage string for command line argument parsing.\n        /// Use ArgumentAttributes to control parsing behaviour.\n        /// Formats the output to the width of the current console window.\n        /// </summary>\n        /// <param name=\"argumentType\"> The type of the arguments to display usage for. </param>\n        /// <returns> Printable string containing a user friendly description of command line arguments. </returns>\n        public static string ArgumentsUsage(Type argumentType)\n        {\n            int screenWidth = Parser.GetConsoleWindowWidth();\n            if (screenWidth == 0)\n                screenWidth = 80;\n            return ArgumentsUsage(argumentType, screenWidth);\n        }\n\n        /// <summary>\n        /// Returns a Usage string for command line argument parsing.\n        /// Use ArgumentAttributes to control parsing behaviour.\n        /// </summary>\n        /// <param name=\"argumentType\"> The type of the arguments to display usage for. </param>\n        /// <param name=\"columns\"> The number of columns to format the output to. </param>\n        /// <returns> Printable string containing a user friendly description of command line arguments. </returns>\n        public static string ArgumentsUsage(Type argumentType, int columns)\n        {\n            return (new Parser(argumentType, null)).GetUsageString(columns);\n        }\n\n        private const int STD_OUTPUT_HANDLE  = -11;\n\n        private struct COORD\n        {\n            internal Int16 x;\n            internal Int16 y;\n        }\n\n        private struct SMALL_RECT\n        {\n            internal Int16 Left;\n            internal Int16 Top;\n            internal Int16 Right;\n            internal Int16 Bottom;\n        }\n\n        private struct CONSOLE_SCREEN_BUFFER_INFO\n        {\n            internal COORD dwSize;\n            internal COORD dwCursorPosition;\n            internal Int16 wAttributes;\n            internal SMALL_RECT srWindow;\n            internal COORD dwMaximumWindowSize;\n        }\n\n        [DllImport(\"kernel32.dll\", EntryPoint=\"GetStdHandle\", SetLastError=true, CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)]\n        private static extern int GetStdHandle(int nStdHandle);\n\n        [DllImport(\"kernel32.dll\", EntryPoint=\"GetConsoleScreenBufferInfo\", SetLastError=true, CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)]\n        private static extern int GetConsoleScreenBufferInfo(int hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);\n\n        /// <summary>\n        /// Returns the number of columns in the current console window\n        /// </summary>\n        /// <returns>Returns the number of columns in the current console window</returns>\n        public static int GetConsoleWindowWidth()\n        {\n            int screenWidth;\n            CONSOLE_SCREEN_BUFFER_INFO csbi = new CONSOLE_SCREEN_BUFFER_INFO();\n\n            int rc;\n            rc = GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), ref csbi);\n            screenWidth = csbi.dwSize.x;\n            return screenWidth;\n        }\n        \n        /// <summary>\n        /// Searches a StringBuilder for a character\n        /// </summary>\n        /// <param name=\"text\"> The text to search. </param>\n        /// <param name=\"value\"> The character value to search for. </param>\n        /// <param name=\"startIndex\"> The index to stat searching at. </param>\n        /// <returns> The index of the first occurence of value or -1 if it is not found. </returns>\n        public static int IndexOf(StringBuilder text, char value, int startIndex)\n        {\n            for (int index = startIndex; index < text.Length; index++)\n            {\n                if (text[index] == value)\n                    return index;\n            }\n\n            return -1;\n        }\n\n        /// <summary>\n        /// Searches a StringBuilder for a character in reverse\n        /// </summary>\n        /// <param name=\"text\"> The text to search. </param>\n        /// <param name=\"value\"> The character to search for. </param>\n        /// <param name=\"startIndex\"> The index to start the search at. </param>\n        /// <returns>The index of the last occurence of value in text or -1 if it is not found. </returns>\n        public static int LastIndexOf(StringBuilder text, char value, int startIndex)\n        {\n            for (int index = Math.Min(startIndex, text.Length - 1); index >= 0; index --)\n            {\n                if (text[index] == value)\n                    return index;\n            }\n            \n            return -1;\n        }\n \n        private const int spaceBeforeParam = 2;\n\n        /// <summary>\n        /// Creates a new command line argument parser.\n        /// </summary>\n        /// <param name=\"argumentSpecification\"> The type of object to  parse. </param>\n        /// <param name=\"reporter\"> The destination for parse errors. </param>\n        public Parser(Type argumentSpecification, ErrorReporter reporter)\n        {\n            this.reporter = reporter;\n            this.arguments = new ArrayList();\n            this.argumentMap = new Hashtable();\n            \n            foreach (FieldInfo field in argumentSpecification.GetFields())\n            {\n                if (!field.IsStatic && !field.IsInitOnly && !field.IsLiteral)\n                {\n                    ArgumentAttribute attribute = GetAttribute(field);\n                    if (attribute is DefaultArgumentAttribute)\n                    {\n                        Debug.Assert(this.defaultArgument == null);\n                        this.defaultArgument = new Argument(attribute, field, reporter);\n                    }\n                    else\n                    {\n                        this.arguments.Add(new Argument(attribute, field, reporter));\n                    }\n                }\n            }\n            \n            // add explicit names to map\n            foreach (Argument argument in this.arguments)\n            {\n                Debug.Assert(!argumentMap.ContainsKey(argument.LongName));\n                this.argumentMap[argument.LongName] = argument;\n                if (argument.ExplicitShortName)\n                {\n                    if (argument.ShortName != null && argument.ShortName.Length > 0)\n                    {\n                        Debug.Assert(!argumentMap.ContainsKey(argument.ShortName));\n                        this.argumentMap[argument.ShortName] = argument;\n                    }\n                    else\n                    {\n                        argument.ClearShortName();\n                    }\n                }\n            }\n            \n            // add implicit names which don't collide to map\n            foreach (Argument argument in this.arguments)\n            {\n                if (!argument.ExplicitShortName)\n                {\n                    if (argument.ShortName != null && argument.ShortName.Length > 0 && !argumentMap.ContainsKey(argument.ShortName))\n                        this.argumentMap[argument.ShortName] = argument;\n                    else\n                        argument.ClearShortName();\n                }\n            }\n        }\n        \n        private static ArgumentAttribute GetAttribute(FieldInfo field)\n        {\n            object[] attributes = field.GetCustomAttributes(typeof(ArgumentAttribute), false);\n            if (attributes.Length == 1)\n                return (ArgumentAttribute) attributes[0];\n\n            Debug.Assert(attributes.Length == 0);\n            return null;\n        }\n        \n        private void ReportUnrecognizedArgument(string argument)\n        {\n            this.reporter(string.Format(\"Unrecognized command line argument '{0}'\", argument));\n        }\n        \n        /// <summary>\n        /// Parses an argument list into an object\n        /// </summary>\n        /// <param name=\"args\"></param>\n        /// <param name=\"destination\"></param>\n        /// <returns> true if an error occurred </returns>\n        private bool ParseArgumentList(string[] args, object destination)\n        {\n            bool hadError = false;\n            if (args != null)\n            {\n                foreach (string argument in args)\n                {\n                    if (argument.Length > 0)\n                    {\n                        switch (argument[0])\n                        {\n                            case '-':\n                            case '/':\n                                int endIndex = argument.IndexOfAny(new char[] {':', '+', '-'}, 1);\n                                string option = argument.Substring(1, endIndex == -1 ? argument.Length - 1 : endIndex - 1);\n                                string optionArgument;\n                                if (option.Length + 1 == argument.Length)\n                                {\n                                    optionArgument = null;\n                                }\n                                else if (argument.Length > 1 + option.Length && argument[1 + option.Length] == ':')\n                                {\n                                    optionArgument = argument.Substring(option.Length + 2);\n                                }\n                                else\n                                {\n                                    optionArgument = argument.Substring(option.Length + 1);\n                                }\n                                \n                                Argument arg = (Argument) this.argumentMap[option];\n                                if (arg == null)\n                                {\n                                    ReportUnrecognizedArgument(argument);\n                                    hadError = true;\n                                }\n                                else\n                                {\n                                    hadError |= !arg.SetValue(optionArgument, destination);\n                                }\n                                break;\n                            case '@':\n                                string[] nestedArguments;\n                                hadError |= LexFileArguments(argument.Substring(1), out nestedArguments);\n                                hadError |= ParseArgumentList(nestedArguments, destination);\n                                break;\n                            default:\n                                if (this.defaultArgument != null)\n                                {\n                                    hadError |= !this.defaultArgument.SetValue(argument, destination);\n                                }\n                                else\n                                {\n                                    ReportUnrecognizedArgument(argument);\n                                    hadError = true;\n                                }\n                                break;\n                        }\n                    }\n                }\n            }\n            \n            return hadError;\n        }\n        \n        /// <summary>\n        /// Parses an argument list.\n        /// </summary>\n        /// <param name=\"args\"> The arguments to parse. </param>\n        /// <param name=\"destination\"> The destination of the parsed arguments. </param>\n        /// <returns> true if no parse errors were encountered. </returns>\n        public bool Parse(string[] args, object destination)\n        {\n            bool hadError = ParseArgumentList(args, destination);\n\n            // check for missing required arguments\n            foreach (Argument arg in this.arguments)\n            {\n                hadError |= arg.Finish(destination);\n            }\n            if (this.defaultArgument != null)\n            {\n                hadError |= this.defaultArgument.Finish(destination);\n            }\n            \n            return !hadError;\n        }\n\n        private struct ArgumentHelpStrings\n        {\n            public ArgumentHelpStrings(string syntax, string help)\n            {\n                this.syntax = syntax;\n                this.help = help;\n            }\n            \n            public string syntax;\n            public string help;\n        }\n        \n        /// <summary>\n        /// A user firendly usage string describing the command line argument syntax.\n        /// </summary>\n        public string GetUsageString(int screenWidth)\n        {\n            ArgumentHelpStrings[] strings = GetAllHelpStrings();\n\n            int maxParamLen = 0;\n            foreach (ArgumentHelpStrings helpString in strings)\n            {\n                maxParamLen = Math.Max(maxParamLen, helpString.syntax.Length);\n            }\n            \n            const int minimumNumberOfCharsForHelpText = 10;\n            const int minimumHelpTextColumn = 5;\n            const int minimumScreenWidth = minimumHelpTextColumn + minimumNumberOfCharsForHelpText;\n\n            int helpTextColumn;\n            int idealMinimumHelpTextColumn = maxParamLen + spaceBeforeParam;\n            screenWidth = Math.Max(screenWidth, minimumScreenWidth);\n            if (screenWidth < (idealMinimumHelpTextColumn + minimumNumberOfCharsForHelpText))\n                helpTextColumn = minimumHelpTextColumn;\n            else\n                helpTextColumn = idealMinimumHelpTextColumn;\n\n            const string newLine = \"\\n\";\n            StringBuilder builder = new StringBuilder();\n            foreach (ArgumentHelpStrings helpStrings in strings)\n            {\n                // add syntax string\n                int syntaxLength = helpStrings.syntax.Length;\n                builder.Append(helpStrings.syntax);\n                \n                // start help text on new line if syntax string is too long\n                int currentColumn = syntaxLength;\n                if (syntaxLength >= helpTextColumn)\n                {\n                    builder.Append(newLine);\n                    currentColumn = 0;\n                }\n                \n                // add help text broken on spaces\n                int charsPerLine = screenWidth - helpTextColumn;\n                int index = 0;\n                while (index < helpStrings.help.Length)\n                {\n                    // tab to start column\n                    builder.Append(' ', helpTextColumn - currentColumn);\n                    currentColumn = helpTextColumn;\n                    \n                    // find number of chars to display on this line\n                    int endIndex = index + charsPerLine;\n                    if (endIndex >= helpStrings.help.Length)\n                    {\n                        // rest of text fits on this line\n                        endIndex = helpStrings.help.Length;\n                    }\n                    else\n                    {\n                        endIndex = helpStrings.help.LastIndexOf(' ', endIndex - 1, Math.Min(endIndex - index, charsPerLine));\n                        if (endIndex <= index)\n                        {\n                            // no spaces on this line, append full set of chars\n                            endIndex = index + charsPerLine;\n                        }\n                    }\n                    \n                    // add chars\n                    builder.Append(helpStrings.help, index, endIndex - index);\n                    index = endIndex;\n                    \n                    // do new line\n                    AddNewLine(newLine, builder, ref currentColumn);\n\n                    // don't start a new line with spaces\n                    while (index < helpStrings.help.Length && helpStrings.help[index] == ' ')\n                        index ++;\n                }\n\n                // add newline if there's no help text                \n                if (helpStrings.help.Length == 0)\n                {\n                    builder.Append(newLine);\n                }\n            }\n            \n            return builder.ToString();\n        }\n        private static void AddNewLine(string newLine, StringBuilder builder, ref int currentColumn)\n        {\n            builder.Append(newLine);\n            currentColumn = 0;\n        }\n        private ArgumentHelpStrings[] GetAllHelpStrings()\n        {\n            ArgumentHelpStrings[] strings = new ArgumentHelpStrings[NumberOfParametersToDisplay()];\n\n            int index = 0;\n            foreach (Argument arg in this.arguments)\n            {\n                strings[index] = GetHelpStrings(arg);\n                index++;\n            }\n            strings[index++] = new ArgumentHelpStrings(\"@<file>\", \"Read response file for more options\");\n            if (this.defaultArgument != null)\n                strings[index++] = GetHelpStrings(this.defaultArgument);\n\n            return strings;\n        }\n        \n        private static ArgumentHelpStrings GetHelpStrings(Argument arg)\n        {\n            return new ArgumentHelpStrings(arg.SyntaxHelp, arg.FullHelpText);\n        }\n        \n        private int NumberOfParametersToDisplay()\n        {\n            int numberOfParameters = this.arguments.Count + 1;\n            if (HasDefaultArgument)\n                numberOfParameters += 1;\n            return numberOfParameters;\n        }\n        \n        /// <summary>\n        /// Does this parser have a default argument.\n        /// </summary>\n        /// <value> Does this parser have a default argument. </value>\n        public bool HasDefaultArgument\n        {\n            get { return this.defaultArgument != null; }\n        }\n\n        private bool LexFileArguments(string fileName, out string[] arguments)\n        {\n            string args  = null;\n                    \n            try\n            {\n                using (FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read))\n                {\n                    args = (new StreamReader(file)).ReadToEnd();\n                }\n            }\n            catch (Exception e)\n            {\n                this.reporter(string.Format(\"Error: Can't open command line argument file '{0}' : '{1}'\", fileName, e.Message));\n                arguments = null;\n                return false;\n            }\n\n            bool hadError = false;                    \n            ArrayList argArray = new ArrayList();\n            StringBuilder currentArg = new StringBuilder();\n            bool inQuotes = false;\n            int index = 0;\n            \n            // while (index < args.Length)\n            try\n            {\n                while (true)\n                {\n                    // skip whitespace\n                    while (char.IsWhiteSpace(args[index]))\n                    {\n                        index += 1;\n                    }\n                    \n                    // # - comment to end of line\n                    if (args[index] == '#')\n                    {\n                        index += 1;\n                        while (args[index] != '\\n')\n                        {\n                            index += 1;\n                        }\n                        continue;\n                    }\n                    \n                    // do one argument\n                    do\n                    {\n                        if (args[index] == '\\\\')\n                        {\n                            int cSlashes = 1;\n                            index += 1;\n                            while (index == args.Length && args[index] == '\\\\')\n                            {\n                                cSlashes += 1;\n                            }\n\n                            if (index == args.Length || args[index] != '\"')\n                            {\n                                currentArg.Append('\\\\', cSlashes);\n                            }\n                            else\n                            {\n                                currentArg.Append('\\\\', (cSlashes >> 1));\n                                if (0 != (cSlashes & 1))\n                                {\n                                    currentArg.Append('\"');\n                                }\n                                else\n                                {\n                                    inQuotes = !inQuotes;\n                                }\n                            }\n                        }\n                        else if (args[index] == '\"')\n                        {\n                            inQuotes = !inQuotes;\n                            index += 1;\n                        }\n                        else\n                        {\n                            currentArg.Append(args[index]);\n                            index += 1;\n                        }\n                    } while (!char.IsWhiteSpace(args[index]) || inQuotes);\n                    argArray.Add(currentArg.ToString());\n                    currentArg.Length = 0;\n                }\n            }\n            catch (System.IndexOutOfRangeException)\n            {\n                // got EOF \n                if (inQuotes)\n                {\n                    this.reporter(string.Format(\"Error: Unbalanced '\\\"' in command line argument file '{0}'\", fileName));\n                    hadError = true;\n                }\n                else if (currentArg.Length > 0)\n                {\n                    // valid argument can be terminated by EOF\n                    argArray.Add(currentArg.ToString());\n                }\n            }\n            \n            arguments = (string[]) argArray.ToArray(typeof (string));\n            return hadError;\n        }\n        \n        private static string LongName(ArgumentAttribute attribute, FieldInfo field)\n        {\n            return (attribute == null || attribute.DefaultLongName) ? field.Name : attribute.LongName;\n        }\n        \n        private static string ShortName(ArgumentAttribute attribute, FieldInfo field)\n        {\n            if (attribute is DefaultArgumentAttribute)\n                return null;\n            if (!ExplicitShortName(attribute))\n                return LongName(attribute, field).Substring(0,1);\n            return attribute.ShortName;\n        }\n        \n        private static string HelpText(ArgumentAttribute attribute, FieldInfo field)\n        {\n            if (attribute == null)\n                return null;\n            else\n                return attribute.HelpText;\n        }\n        \n        private static bool HasHelpText(ArgumentAttribute attribute)\n        {\n            return (attribute != null && attribute.HasHelpText);\n        }\n        \n        private static bool ExplicitShortName(ArgumentAttribute attribute)\n        {\n            return (attribute != null && !attribute.DefaultShortName);\n        }\n\n        private static object DefaultValue(ArgumentAttribute attribute, FieldInfo field)\n        {\n            return (attribute == null || !attribute.HasDefaultValue) ? null : attribute.DefaultValue;\n        }\n\n        private static Type ElementType(FieldInfo field)\n        {\n            if (IsCollectionType(field.FieldType))\n                return field.FieldType.GetElementType();\n            else\n                return null;\n        }\n        \n        private static ArgumentType Flags(ArgumentAttribute attribute, FieldInfo field)\n        {\n            if (attribute != null)\n                return attribute.Type;\n            else if (IsCollectionType(field.FieldType))\n                return ArgumentType.MultipleUnique;\n            else\n                return ArgumentType.AtMostOnce;\n        }\n        \n        private static bool IsCollectionType(Type type)\n        {\n            return type.IsArray;\n        }\n            \n        private static bool IsValidElementType(Type type)\n        {\n            return type != null && (\n                type == typeof(int) ||\n                type == typeof(uint) ||\n                type == typeof(string) ||\n                type == typeof(bool) ||\n                type.IsEnum);\n        }\n        \n        [System.Diagnostics.DebuggerDisplay(\"Name = {LongName}\")]\n        private class Argument\n        {\n            public Argument(ArgumentAttribute attribute, FieldInfo field, ErrorReporter reporter)\n            {\n                this.longName = Parser.LongName(attribute, field);\n                this.explicitShortName = Parser.ExplicitShortName(attribute);\n                this.shortName = Parser.ShortName(attribute, field);\n                this.hasHelpText = Parser.HasHelpText(attribute);\n                this.helpText = Parser.HelpText(attribute, field);\n                this.defaultValue = Parser.DefaultValue(attribute, field);\n                this.elementType = ElementType(field);\n                this.flags = Flags(attribute, field);\n                this.field = field;\n                this.seenValue = false;\n                this.reporter = reporter;\n                this.isDefault = attribute != null && attribute is DefaultArgumentAttribute;\n                \n                if (IsCollection)\n                {\n                    this.collectionValues = new ArrayList();\n                }\n                \n                Debug.Assert(this.longName != null && this.longName != \"\");\n                Debug.Assert(!this.isDefault || !this.ExplicitShortName);\n                Debug.Assert(!IsCollection || AllowMultiple, \"Collection arguments must have allow multiple\");\n                Debug.Assert(!Unique || IsCollection, \"Unique only applicable to collection arguments\");\n                Debug.Assert(IsValidElementType(Type) ||\n                    IsCollectionType(Type));\n                Debug.Assert((IsCollection && IsValidElementType(elementType)) ||\n                    (!IsCollection && elementType == null));\n                Debug.Assert(!(this.IsRequired && this.HasDefaultValue), \"Required arguments cannot have default value\");\n                Debug.Assert(!this.HasDefaultValue || (this.defaultValue.GetType() == field.FieldType), \"Type of default value must match field type\");\n            }\n            \n            public bool Finish(object destination)\n            {\n                if (this.SeenValue)\n                {\n                    if (this.IsCollection)\n                    {\n                        this.field.SetValue(destination, this.collectionValues.ToArray(this.elementType));\n                    }\n                }\n                else\n                {\n                    if (this.HasDefaultValue)\n                    {\n                        this.field.SetValue(destination, this.DefaultValue);\n                    }\n                }\n                \n                return ReportMissingRequiredArgument();\n            }\n            \n            private bool ReportMissingRequiredArgument()\n            {\n                if (this.IsRequired && !this.SeenValue)\n                {\n                    if (this.IsDefault)\n                        reporter(string.Format(\"Missing required argument '<{0}>'.\", this.LongName));\n                    else\n                        reporter(string.Format(\"Missing required argument '/{0}'.\", this.LongName));\n                    return true;\n                }\n                return false;\n            }\n            \n            private void ReportDuplicateArgumentValue(string value)\n            {\n                this.reporter(string.Format(\"Duplicate '{0}' argument '{1}'\", this.LongName, value));\n            }\n            \n            public bool SetValue(string value, object destination)\n            {\n                if (SeenValue && !AllowMultiple)\n                {\n                    this.reporter(string.Format(\"Duplicate '{0}' argument\", this.LongName));\n                    return false;\n                }\n                this.seenValue = true;\n                \n                object newValue;\n                if (!ParseValue(this.ValueType, value, out newValue))\n                    return false;\n                if (this.IsCollection)\n                {\n                    if (this.Unique && this.collectionValues.Contains(newValue))\n                    {\n                        ReportDuplicateArgumentValue(value);\n                        return false;\n                    }\n                    else\n                    {\n                        this.collectionValues.Add(newValue);\n                    }\n                }\n                else\n                {\n                    this.field.SetValue(destination, newValue);\n                }\n                \n                return true;\n            }\n            \n            public Type ValueType\n            {\n                get { return this.IsCollection ? this.elementType : this.Type; }\n            }\n            \n            private void ReportBadArgumentValue(string value)\n            {\n                this.reporter(string.Format(\"'{0}' is not a valid value for the '{1}' command line option\", value, this.LongName));\n            }\n            \n            private bool ParseValue(Type type, string stringData, out object value)\n            {\n                // null is only valid for bool variables\n                // empty string is never valid\n                if ((stringData != null || type == typeof(bool)) && (stringData == null || stringData.Length > 0))\n                {\n                    try\n                    {\n                        if (type == typeof(string))\n                        {\n                            value = stringData;\n                            return true;\n                        }\n                        else if (type == typeof(bool))\n                        {\n                            if (stringData == null || stringData == \"+\")\n                            {\n                                value = true;\n                                return true;\n                            }\n                            else if (stringData == \"-\")\n                            {\n                                value = false;\n                                return true;\n                            }\n                        }\n                        else if (type == typeof(int))\n                        {\n                            value = int.Parse(stringData);\n                            return true;\n                        }\n                        else if (type == typeof(uint))\n                        {\n                            value = int.Parse(stringData);\n                            return true;\n                        }\n                        else\n                        {\n                            Debug.Assert(type.IsEnum);\n\n                            bool valid = false;\n                            foreach (string name in Enum.GetNames(type))\n                            {\n                                if (name == stringData)\n                                {\n                                    valid = true;\n                                    break;\n                                }\n                            }\n                            if (valid)\n                            {\n                                value = Enum.Parse(type, stringData, true);\n                                return true;\n                            }\n                        }\n                    }\n                    catch\n                    {\n                        // catch parse errors\n                    }\n                }\n                                \n                ReportBadArgumentValue(stringData);\n                value = null;\n                return false;\n            }\n            \n            private void AppendValue(StringBuilder builder, object value)\n            {\n                if (value is string || value is int || value is uint || value.GetType().IsEnum)\n                {\n                    builder.Append(value.ToString());\n                }\n                else if (value is bool)\n                {\n                    builder.Append((bool) value ? \"+\" : \"-\");\n                }\n                else\n                {\n                    bool first = true;\n                    foreach (object o in (System.Array) value)\n                    {\n                        if (!first)\n                        {\n                            builder.Append(\", \");\n                        }\n                        AppendValue(builder, o);\n                        first = false;\n                    }\n                }\n            }\n            \n            public string LongName\n            {\n                get { return this.longName; }\n            }\n\n            public bool ExplicitShortName\n            {\n                get { return this.explicitShortName; }\n            }\n            \n            public string ShortName\n            {\n                get { return this.shortName; }\n            }\n            \n            public bool HasShortName\n            {\n                get { return this.shortName != null; }\n            }\n            \n            public void ClearShortName()\n            {\n                this.shortName = null;\n            }\n\n            public bool HasHelpText\n            {\n                get { return this.hasHelpText; }\n            }\n            \n            public string HelpText\n            {\n                get { return this.helpText; }\n            }\n            \n            public object DefaultValue\n            {\n                get { return this.defaultValue; }\n            }\n            \n            public bool HasDefaultValue\n            {\n                get { return null != this.defaultValue; }\n            }\n\n            public string FullHelpText\n            {\n                get {\n                    StringBuilder builder = new StringBuilder();\n                    if (this.HasHelpText)\n                    {\n                        builder.Append(this.HelpText);\n                    }\n                    if (this.HasDefaultValue)\n                    {\n                        if (builder.Length > 0)\n                            builder.Append(\" \");\n                        builder.Append(\"Default value:'\");\n                        AppendValue(builder, this.DefaultValue);\n                        builder.Append('\\'');\n                    }\n                    if (this.HasShortName)\n                    {\n                        if (builder.Length > 0)\n                            builder.Append(\" \");\n                        builder.Append(\"(short form /\");\n                        builder.Append(this.ShortName);\n                        builder.Append(\")\");\n                    }\n                    return builder.ToString();\n                }\n            }\n\n            public string SyntaxHelp\n            {\n                get\n                {\n                    StringBuilder builder = new StringBuilder();\n\n                    if (this.IsDefault)\n                    {\n                        builder.Append(\"<\");\n                        builder.Append(this.LongName);\n                        builder.Append(\">\");\n                    }\n                    else\n                    {\n                        builder.Append(\"/\");\n                        builder.Append(this.LongName);\n                        Type valueType = this.ValueType;\n                        if (valueType == typeof(int))\n                        {\n                            builder.Append(\":<int>\");\n                        }\n                        else if (valueType == typeof(uint))\n                        {\n                            builder.Append(\":<uint>\");\n                        }\n                        else if (valueType == typeof(bool))\n                        {\n                            builder.Append(\"[+|-]\");\n                        }\n                        else if (valueType == typeof(string))\n                        {\n                            builder.Append(\":<string>\");\n                        }\n                        else\n                        {\n                            Debug.Assert(valueType.IsEnum);\n\n                            builder.Append(\":{\");\n                            bool first = true;\n                            foreach (FieldInfo field in valueType.GetFields())\n                            {\n                                if (field.IsStatic)\n                                {\n                                    if (first)\n                                        first = false;\n                                    else\n                                        builder.Append('|');\n                                    builder.Append(field.Name);\n                                }\n                            }\n                            builder.Append('}');\n                        }\n                    }\n                    \n                    return builder.ToString();\n                }\n            }\n\n            public bool IsRequired\n            {\n                get { return 0 != (this.flags & ArgumentType.Required); }\n            }\n            \n            public bool SeenValue\n            {\n                get { return this.seenValue; }\n            }\n            \n            public bool AllowMultiple\n            {\n                get { return 0 != (this.flags & ArgumentType.Multiple); }\n            }\n            \n            public bool Unique\n            {\n                get { return 0 != (this.flags & ArgumentType.Unique); }\n            }\n            \n            public Type Type\n            {\n                get { return field.FieldType; }\n            }\n            \n            public bool IsCollection\n            {\n                get { return IsCollectionType(Type); }\n            }\n            \n            public bool IsDefault\n            {\n                get { return this.isDefault; }\n            }\n            \n            private string longName;\n            private string shortName;\n            private string helpText;\n            private bool hasHelpText;\n            private bool explicitShortName;\n            private object defaultValue;\n            private bool seenValue;\n            private FieldInfo field;\n            private Type elementType;\n            private ArgumentType flags;\n            private ArrayList collectionValues;\n            private ErrorReporter reporter;\n            private bool isDefault;\n        }\n        \n        private ArrayList arguments;\n        private Hashtable argumentMap;\n        private Argument defaultArgument;\n        private ErrorReporter reporter;\n    }\n}"
  },
  {
    "path": "Examples/SingleProcess/Program.cs",
    "content": "﻿// SharedMemory (File: SingleProcess\\Program.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading;\nusing CommandLine;\n#if NET40Plus\nusing System.Threading.Tasks;\n#endif\n\nnamespace SingleProcess\n{\n    class Program\n    {\n        class AppArguments\n        {\n            [Argument(ArgumentType.AtMostOnce, ShortName = \"b\", DefaultValue = 1048576, HelpText = \"The buffer size.\")]\n            public int bufferSize = 0;\n            [Argument(ArgumentType.AtMostOnce, ShortName = \"n\", DefaultValue = 50, HelpText = \"The number of nodes.\")]\n            public int nodeCount = 0;\n            [Argument(ArgumentType.Required, ShortName = \"w\", HelpText = \"The number of writers.\")]\n            public int writers = 0;\n            [Argument(ArgumentType.Required, ShortName = \"r\", HelpText = \"The number of readers.\")]\n            public int readers = 0;\n            [Argument(ArgumentType.AtMostOnce, ShortName = \"e\", DefaultValue = 100000, HelpText = \"The number of elements to process.\")]\n            public int elements = 0;\n        }\n\n        static void Main(string[] args)\n        {\n            int elements = 100000;\n            int writeCount = 0;\n            int clientWaitCount = 0;\n            int readCount = 0;\n            int serverWaitCount = 0;\n            long lastTick = 0;\n            int bufferSize = 1048576;\n            int size = sizeof(byte) * bufferSize;\n            int count = 50; // node count within buffer\n\n            int serverCount = 0;\n            int clientCount = 0;\n\n            // Process command line\n            AppArguments parsedArgs = new AppArguments();\n            var validArgs = Parser.ParseArgumentsWithUsage(args, parsedArgs);\n\n            if (!validArgs)\n            {\n                return;\n            }\n            else\n            {\n                elements = parsedArgs.elements;\n                bufferSize = parsedArgs.bufferSize;\n                serverCount = parsedArgs.writers;\n                clientCount = parsedArgs.readers;\n                size = sizeof(byte) * bufferSize;\n                count = parsedArgs.nodeCount;\n            }\n\n            \n            Console.WriteLine(\"Node buffer size: {0}, count: {1}, writers: {2}, readers {3}, elements: {4}\", size, count, serverCount, clientCount, elements);\n\n            int dataListCount = 256;\n            // Generate random data to be written\n            Random random = new Random();\n            byte[][] dataList = new byte[dataListCount][];\n            for (var j = 0; j < dataListCount; j++)\n            {\n                var data = new byte[size];\n                random.NextBytes(data);\n                dataList[j] = data;\n            }\n\n            long bytesWritten = 0;\n            long bytesRead = 0;\n            string name = Guid.NewGuid().ToString();\n            var server = new SharedMemory.CircularBuffer(name, count, size);\n            \n            Stopwatch sw = Stopwatch.StartNew();\n\n            Action clientAction = () =>\n            {\n                byte[] testData = new byte[size];\n\n                var client = new SharedMemory.CircularBuffer(name);\n\n                Stopwatch clientTime = new Stopwatch();\n                clientTime.Start();\n                long startTick = 0;\n                long stopTick = 0;\n\n                for (; ; )\n                {\n                    startTick = clientTime.ElapsedTicks;\n                    int amount = client.Read(testData, 100);\n                    bytesRead += amount;\n                    if (amount == 0)\n                        Interlocked.Increment(ref clientWaitCount);\n                    else\n                        Interlocked.Increment(ref readCount);\n                    stopTick = clientTime.ElapsedTicks;\n\n                    if (writeCount > elements && writeCount - readCount == 0)\n                        break;\n                }\n            };\n            for (int c = 0; c < clientCount; c++)\n            {\n#if NET40Plus\n                Task c1 = Task.Factory.StartNew(clientAction);\n#else\n                ThreadPool.QueueUserWorkItem((o) => { clientAction(); });\n#endif\n            }\n            bool wait = true;\n            int index = 0;\n            Action serverWrite = () =>\n            {\n                int serverIndex = Interlocked.Increment(ref index);\n\n                var writer = (serverIndex == 1 ? server : new SharedMemory.CircularBuffer(name));\n                bool done = false;\n                TimeSpan doneTime = TimeSpan.MinValue;\n                for (; ; )\n                {\n                    if (writeCount <= elements)\n                    {\n                        int amount = writer.Write(dataList[random.Next(0, dataListCount)], 100);\n                        bytesWritten += amount;\n                        if (amount == 0)\n                            Interlocked.Increment(ref serverWaitCount);\n                        else\n                            Interlocked.Increment(ref writeCount);\n                    }\n                    else\n                    {\n                        if (!done && serverIndex == 1)\n                        {\n                            doneTime = sw.Elapsed;\n                            done = true;\n                        }\n                    }\n\n                    if (serverIndex == 1 && sw.ElapsedTicks - lastTick > 1000000)\n                    {\n                        Console.WriteLine(\"Write: {0}, Read: {1}, Diff: {5}, Wait(cli/svr): {3}/{2}, {4}MB/s\", writeCount, readCount, serverWaitCount, clientWaitCount, (int)((((bytesWritten + bytesRead) / 1048576.0) / sw.ElapsedMilliseconds) * 1000), writeCount - readCount);\n                        lastTick = sw.ElapsedTicks;\n                        if (writeCount > elements && writeCount - readCount == 0)\n                        {\n                            Console.WriteLine(\"Total Time: \" + doneTime);\n                            wait = false;\n                            break;\n                        }\n                    }\n                }\n            };\n\n            for (int s = 0; s < serverCount; s++)\n            {\n#if NET40Plus\n                Task s1 = Task.Factory.StartNew(serverWrite);\n#else\n                ThreadPool.QueueUserWorkItem((o) => { serverWrite(); });\n#endif\n            }\n            while (wait)\n                Thread.Sleep(100);\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/SingleProcess/SingleProcess.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFrameworks>netcoreapp3.0;netcoreapp2.0;net47;net46;net45;net4;net35</TargetFrameworks>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\SharedMemory\\SharedMemory.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "LICENSE.md",
    "content": "SharedMemory\n\nCopyright (c) 2014-2020 Justin Stenning\nhttp://spazzarama.com\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\nall copies 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\nTHE SOFTWARE.\n\nThe SharedMemory library was inspired by the following Code Project article:\n  \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n  http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\n"
  },
  {
    "path": "README.md",
    "content": "SharedMemory\n============\n\nC# shared memory classes for sharing data between processes (Array, Buffer, Circular Buffer and RPC)\n\n[![Build status](https://ci.appveyor.com/api/projects/status/uc32kwm1281y4sie?svg=true)](https://ci.appveyor.com/project/spazzarama/sharedmemory)\n\nAbout\n-----\n\nThe SharedMemory class library provides a set of C# classes that utilise memory mapped files for fast low-level inter-process communication (IPC). Originally only for sharing data between processes, but now also with a simple RPC implementation.\n\nThe library uses the .NET MemoryMappedFile class in .NET 4.0+, and implements its own wrapper class for .NET 3.5.\n\nClasses\n-------\n\n * `SharedMemory.SharedBuffer` - an abstract base class that wraps a memory mapped file, exposing the read/write operations and implementing a small header to allow clients to open the shared buffer without knowing the size beforehand.\n * `SharedMemory.BufferWithLocks` - an abstract class that extends SharedMemory.SharedBuffer to provide simple read/write locking support through the use of EventWaitHandles.\n * `SharedMemory.SharedArray` - a simple generic array implementation utilising a shared memory buffer. Inherits from SharedMemory.BufferWithLocks to provide support for thread synchronisation.\n * `SharedMemory.BufferReadWrite` - provides read/write access to a shared memory buffer, with various overloads to support reading and writing structures, copying to and from IntPtr and so on. Inherits from SharedMemory.BufferWithLocks to provide support for thread synchronisation.\n * `SharedMemory.CircularBuffer` - lock-free FIFO circular buffer implementation (aka ring buffer). Supporting 2 or more nodes, this implementation supports multiple readers and writers. The lock-free approach is implemented using Interlocked.Exchange and EventWaitHandles.\n * `SharedMemory.RpcBuffer` - simple bi-directional RPC channel using `SharedMemory.CircularBuffer`. Supports a master+slave pair per channel. Only available in .NET 4.5+ / .NET Standard 2.0\n\nExample Usage\n-------------\n\nThe output from the of the following examples is:\n\n    SharedMemory.SharedArray:\n    123\n    456\n    SharedMemory.CircularBuffer:\n    123\n    456\n    SharedMemory.BufferReadWrite:\n    123\n    456\n    SharedMemory.RpcBuffer:\n    133\n\n**SharedMemory.SharedArray**\n\n    Console.WriteLine(\"SharedMemory.SharedArray:\");\n    using (var producer = new SharedMemory.SharedArray<int>(\"MySharedArray\", 10))\n    using (var consumer = new SharedMemory.SharedArray<int>(\"MySharedArray\"))\n    {\n        producer[0] = 123;\n        producer[producer.Length - 1] = 456;\n        \n        Console.WriteLine(consumer[0]);\n        Console.WriteLine(consumer[consumer.Length - 1]);\n    }\n\n**SharedMemory.CircularBuffer**\n\n    Console.WriteLine(\"SharedMemory.CircularBuffer:\");\n    using (var producer = new SharedMemory.CircularBuffer(name: \"MySharedMemory\", nodeCount: 3, nodeBufferSize: 4))\n    using (var consumer = new SharedMemory.CircularBuffer(name: \"MySharedMemory\"))\n    {\n        // nodeCount must be one larger than the number\n        // of writes that must fit in the buffer at any one time\n        producer.Write<int>(new int[] { 123 });\n        producer.Write<int>(new int[] { 456 });\n       \n        int[] data = new int[1];\n        consumer.Read<int>(data);\n        Console.WriteLine(data[0]);\n        consumer.Read<int>(data);\n        Console.WriteLine(data[0]);\n    }\n\n**SharedMemory.BufferReadWrite**\n\n    Console.WriteLine(\"SharedMemory.BufferReadWrite:\");\n    using (var producer = new SharedMemory.BufferReadWrite(name: \"MySharedBuffer\", bufferSize: 1024))\n    using (var consumer = new SharedMemory.BufferReadWrite(name: \"MySharedBuffer\"))\n    {\n        int data = 123;\n        producer.Write<int>(ref data);\n        data = 456;\n        producer.Write<int>(ref data, 1000);\n        \n        int readData;\n        consumer.Read<int>(out readData);\n        Console.WriteLine(readData);\n        consumer.Read<int>(out readData, 1000);\n        Console.WriteLine(readData);\n    }\n\n**SharedMemory.RpcBuffer**\n\n    Console.WriteLine(\"SharedMemory.RpcBuffer:\");\n    // Ensure a unique channel name\n    var rpcName = \"RpcTest\" + Guid.NewGuid().ToString();\n    var rpcMaster = new RpcBuffer(rpcName);\n    var rpcSlave = new RpcBuffer(rpcName, (msgId, payload) =>\n    {\n        // Add the two bytes together\n        return BitConverter.GetBytes((payload[0] + payload[1]));\n    });\n    \n    // Call the remote handler to add 123 and 10\n    var result = rpcMaster.RemoteRequest(new byte[] { 123, 10 });\n    Console.WriteLine(result); // outputs 133\n\nPerformance\n-----------\n\n### RPC Buffer\n\nWhen an `RpcBuffer` is created, a buffer capacity can be specified along with the number of nodes to be created in the underlying `CircularBuffer` instances. A message is sent within one or more packets, where a single packet is made up of a packet header (64-bytes for `RpcProtocol.V1`) and the message payload. Ideally there should be enough room allocated within the underlying buffer to hold at least one message preferably a few (i.e. `bufferCapacity * numberOfNodes > maxMessageSize`).\n\nIf the message payload exceeds the `bufferCapacity - packetHeaderSize`, then the message is split into multiple packets. Therefore the `RpcBuffer` message throughput depends not only upon the message size, but the relationship between the buffer capacity and the message size (i.e. how many packets are required for a single message).\n\nFor example, a message size of 512KB that fits in a single packet (i.e. with a `bufferCapacity >= 1024 * 500 + 64`) might achieve a throughput of around 2k messages/sec whereas with a buffer capacity of only 1KB it will achieve around 500 messages/sec. Larger buffer capacities do not necessarily mean greater message throughput, for example with the 512KB message size example, using a smaller buffer capacity of 256KB actually slightly improves performance.\n\nA 1KB message can be sent as a single packet approximately 10k times/sec.\n\n### Circular Buffer\n\nThe maximum bandwidth achieved was approximately 20GB/s, using 20 nodes of 1MB each with 1 reader and 1 writer. The .NET 3.5 implementation is markedly slower (~14GB/s), probably due to framework level performance improvements.\n\nThe following chart shows the bandwidth achieved in MB/s using a variety of circular buffer configurations, ranging from 2 nodes to 50 nodes with a varying number of readers/writers, comparing a 1KB vs 1MB node buffer size on .NET 3.5 and .NET 4.\n\n![Circular buffer bandwidth](http://spazzarama.com/wp-content/uploads/2015/12/SharedMemoryBandwidth.png)\n\nAll results are from a machine running Windows 10 64-bit, Intel Core i7-3770K @ 3.50GHz, 16GB DDR3@1200MHz on an ASUS P8Z77-V motherboard. The data transferred was selected randomly from an array of 256 buffers that had been populated with random bytes.\n"
  },
  {
    "path": "SharedMemory/BufferReadWrite.cs",
    "content": "﻿// SharedMemory (File: SharedMemory\\bufferreadwrite.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Security.Permissions;\nusing System.Text;\n\nnamespace SharedMemory\n{\n    /// <summary>\n    /// Read/Write buffer with support for simple inter-process read/write synchronisation.\n    /// </summary>\n#if NETFULL\n    [PermissionSet(SecurityAction.LinkDemand)]\n#endif\n    public unsafe class BufferReadWrite : BufferWithLocks\n    {\n        #region Constructors\n\n        /// <summary>\n        /// Creates a new shared memory buffer with the specified name and size\n        /// </summary>\n        /// <param name=\"name\">The name of the shared memory to create</param>\n        /// <param name=\"bufferSize\">The size of the buffer</param>\n        public BufferReadWrite(string name, int bufferSize)\n            : base(name, bufferSize, true)\n        {\n            Open();\n        }\n\n        /// <summary>\n        /// Opens an existing shared memory buffer with the specified name\n        /// </summary>\n        /// <param name=\"name\">The name of the shared memory to open</param>\n        public BufferReadWrite(string name)\n            : base(name, 0, false)\n        {\n            Open();\n        }\n\n        #endregion\n\n        #region Writing\n\n        /// <summary>\n        /// Writes an instance of <typeparamref name=\"T\"/> into the buffer\n        /// </summary>\n        /// <typeparam name=\"T\">A structure type</typeparam>\n        /// <param name=\"data\">A reference to an instance of <typeparamref name=\"T\"/> to be written</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to write to.</param>\n        [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Design\", \"CA1061:DoNotHideBaseClassMethods\")]\n        new public void Write<T>(ref T data, long bufferPosition = 0)\n            where T : struct\n        {\n            base.Write(ref data, bufferPosition);\n        }\n\n        /// <summary>\n        /// Writes an array of <typeparamref name=\"T\"/> into the buffer\n        /// </summary>\n        /// <typeparam name=\"T\">A structure type</typeparam>\n        /// <param name=\"buffer\">An array of <typeparamref name=\"T\"/> to be written. The length of this array controls the number of elements to be written.</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to write to.</param>\n        [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Design\", \"CA1061:DoNotHideBaseClassMethods\")]\n        new public void Write<T>(T[] buffer, long bufferPosition = 0)\n            where T : struct\n        {\n            base.Write(buffer, bufferPosition);\n        }\n\n        /// <summary>\n        /// Writes <paramref name=\"length\"/> bytes from the <paramref name=\"ptr\"/> into the shared memory buffer.\n        /// </summary>\n        /// <param name=\"ptr\">A managed pointer to the memory location to be copied into the buffer</param>\n        /// <param name=\"length\">The number of bytes to be copied</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to write to.</param>\n        [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Design\", \"CA1061:DoNotHideBaseClassMethods\")]\n        new public void Write(IntPtr ptr, int length, long bufferPosition = 0)\n        {\n            base.Write(ptr, length, bufferPosition);\n        }\n\n        /// <summary>\n        /// Prepares an IntPtr to the buffer position and calls <paramref name=\"writeFunc\"/> to perform the writing.\n        /// </summary>\n        /// <param name=\"writeFunc\">A function used to write to the buffer. The IntPtr parameter is a pointer to the buffer offset by <paramref name=\"bufferPosition\"/>.</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region to start writing from.</param>\n        [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Design\", \"CA1061:DoNotHideBaseClassMethods\")]\n        new public void Write(Action<IntPtr> writeFunc, long bufferPosition = 0)\n        {\n            base.Write(writeFunc, bufferPosition);\n        }\n\n        #endregion\n\n        #region Reading\n\n        /// <summary>\n        /// Reads an instance of <typeparamref name=\"T\"/> from the buffer\n        /// </summary>\n        /// <typeparam name=\"T\">A structure type</typeparam>\n        /// <param name=\"data\">Output parameter that will contain the value read from the buffer</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to read from.</param>\n        [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Design\", \"CA1061:DoNotHideBaseClassMethods\")]\n        new public void Read<T>(out T data, long bufferPosition = 0)\n            where T : struct\n        {\n            base.Read(out data, bufferPosition);\n        }\n\n        /// <summary>\n        /// Reads an array of <typeparamref name=\"T\"/> from the buffer\n        /// </summary>\n        /// <typeparam name=\"T\">A structure type</typeparam>\n        /// <param name=\"buffer\">Array that will contain the values read from the buffer. The length of this array controls the number of elements to read.</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to read from.</param>\n        [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Design\", \"CA1061:DoNotHideBaseClassMethods\")]\n        new public void Read<T>(T[] buffer, long bufferPosition = 0)\n            where T : struct\n        {\n            base.Read(buffer, bufferPosition);\n        }\n\n        /// <summary>\n        /// Reads <paramref name=\"length\"/> bytes into the memory location <paramref name=\"destination\"/> from the shared memory buffer.\n        /// </summary>\n        /// <param name=\"destination\">A managed pointer to the memory location to copy data into from the buffer</param>\n        /// <param name=\"length\">The number of bytes to be copied</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to read from.</param>\n        [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Design\", \"CA1061:DoNotHideBaseClassMethods\")]\n        new public void Read(IntPtr destination, int length, long bufferPosition = 0)\n        {\n            base.Read(destination, length, bufferPosition);\n        }\n\n        /// <summary>\n        /// Prepares an IntPtr to the buffer position and calls <paramref name=\"readFunc\"/> to perform the reading.\n        /// </summary>\n        /// <param name=\"readFunc\">A function used to read from the buffer. The IntPtr parameter is a pointer to the buffer offset by <paramref name=\"bufferPosition\"/>.</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to read from.</param>\n        [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Design\", \"CA1061:DoNotHideBaseClassMethods\")]\n        new public void Read(Action<IntPtr> readFunc, long bufferPosition = 0)\n        {\n            base.Read(readFunc, bufferPosition);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SharedMemory/BufferWithLocks.cs",
    "content": "﻿// SharedMemory (File: SharedMemory\\BufferWithLocks.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Security.Permissions;\nusing System.Text;\nusing System.Threading;\n\nnamespace SharedMemory\n{\n    /// <summary>\n    /// <para>Extends <see cref=\"SharedBuffer\"/> to support simple thread-synchronisation for read/write \n    /// to the buffer by allowing callers to acquire and release read/write locks.</para>\n    /// <para>All buffer read/write operations have been overloaded to first perform a <see cref=\"System.Threading.WaitHandle.WaitOne()\"/> \n    /// using the <see cref=\"ReadWaitEvent\"/> and <see cref=\"WriteWaitEvent\"/> respectively.</para>\n    /// <para>By default all read/write operations will not block, it is necessary to first acquire locks \n    /// through calls to <see cref=\"AcquireReadLock\"/> and <see cref=\"AcquireWriteLock\"/> as appropriate, with corresponding \n    /// calls to <see cref=\"ReleaseReadLock\"/> and <see cref=\"ReleaseWriteLock\"/> to release the locks.</para>\n    /// </summary>\n#if NETFULL\n    [PermissionSet(SecurityAction.LinkDemand)]\n    [PermissionSet(SecurityAction.InheritanceDemand)]\n#endif\n    public abstract class BufferWithLocks : SharedBuffer\n    {\n        /// <summary>\n        /// An event handle used for blocking write operations.\n        /// </summary>\n        protected EventWaitHandle WriteWaitEvent { get; private set; }\n        \n        /// <summary>\n        /// An event handle used for blocking read operations.\n        /// </summary>\n        protected EventWaitHandle ReadWaitEvent { get; private set; }\n\n        #region Constructors\n\n        /// <summary>\n        /// Create a new <see cref=\"BufferWithLocks\"/> instance with the specified name and buffer size.\n        /// </summary>\n        /// <param name=\"name\">The name of the shared memory</param>\n        /// <param name=\"bufferSize\">The buffer size in bytes.</param>\n        /// <param name=\"ownsSharedMemory\">Whether or not the current instance owns the shared memory. If true a new shared memory will be created and initialised otherwise an existing one is opened.</param>\n        protected BufferWithLocks(string name, long bufferSize, bool ownsSharedMemory)\n            : base(name, bufferSize, ownsSharedMemory)\n        {\n            WriteWaitEvent = new EventWaitHandle(true, EventResetMode.ManualReset, Name + \"_evt_write\");\n            ReadWaitEvent = new EventWaitHandle(true, EventResetMode.ManualReset, Name + \"_evt_read\");\n        }\n\n        #endregion\n\n        #region Synchronisation\n\n        private int _readWriteTimeout = 100;\n\n        /// <summary>\n        /// The Read/Write operation timeout in milliseconds (to prevent deadlocks). Defaults to 100ms and must be larger than -1.\n        /// If a Read or Write operation's WaitEvent does not complete within this timeframe a <see cref=\"TimeoutException\"/> will be thrown.\n        /// If using AcquireReadLock/ReleaseReadLock and AcquireWriteLock/ReleaseWriteLock correctly this timeout will never occur.\n        /// </summary>\n        public virtual int ReadWriteTimeout\n        {\n            get { return _readWriteTimeout; }\n            set\n            {\n                if (value < 0)\n                    throw new ArgumentOutOfRangeException(\"ReadWriteTimeout\", \"Must be larger than -1.\");\n                _readWriteTimeout = value;\n            }\n        }\n\n        /// <summary>\n        /// Blocks the current thread until it is able to acquire a read lock. If successful all subsequent writes will be blocked until after a call to <see cref=\"ReleaseReadLock\"/>.\n        /// </summary>\n        /// <param name=\"millisecondsTimeout\">The number of milliseconds to wait, or <see cref=\"System.Threading.Timeout.Infinite\" /> (-1) to wait indefinitely.</param>\n        /// <returns>true if the read lock was able to be acquired, otherwise false.</returns>\n        /// <exception cref=\"System.ArgumentOutOfRangeException\"><paramref name=\"millisecondsTimeout\"/> is a negative number other than -1, which represents an infinite time-out.</exception>\n        /// <remarks>If <paramref name=\"millisecondsTimeout\"/> is <see cref=\"System.Threading.Timeout.Infinite\" /> (-1), then attempting to acquire a read lock after acquiring a write lock on the same thread will result in a deadlock.</remarks>\n        public bool AcquireReadLock(int millisecondsTimeout = System.Threading.Timeout.Infinite)\n        {\n            if (!ReadWaitEvent.WaitOne(millisecondsTimeout))\n                return false;\n            WriteWaitEvent.Reset();\n            return true;\n        }\n\n        /// <summary>\n        /// Releases the current read lock, allowing all blocked writes to continue.\n        /// </summary>\n        public void ReleaseReadLock()\n        {\n            WriteWaitEvent.Set();\n        }\n\n        /// <summary>\n        /// Blocks the current thread until it is able to acquire a write lock. If successful all subsequent reads will be blocked until after a call to <see cref=\"ReleaseWriteLock\"/>.\n        /// </summary>\n        /// <param name=\"millisecondsTimeout\">The number of milliseconds to wait, or System.Threading.Timeout.Infinite (-1) to wait indefinitely.</param>\n        /// <returns>true if the write lock was able to be acquired, otherwise false.</returns>\n        /// <exception cref=\"System.ArgumentOutOfRangeException\"><paramref name=\"millisecondsTimeout\"/> is a negative number other than -1, which represents an infinite time-out.</exception>\n        /// <remarks>If <paramref name=\"millisecondsTimeout\"/> is <see cref=\"System.Threading.Timeout.Infinite\" /> (-1), then attempting to acquire a write lock after acquiring a read lock on the same thread will result in a deadlock.</remarks>\n        public bool AcquireWriteLock(int millisecondsTimeout = System.Threading.Timeout.Infinite)\n        {\n            if (!WriteWaitEvent.WaitOne(millisecondsTimeout))\n                return false;\n            ReadWaitEvent.Reset();\n            return true;\n        }\n\n        /// <summary>\n        /// Releases the current write lock, allowing all blocked reads to continue.\n        /// </summary>\n        public void ReleaseWriteLock()\n        {\n            ReadWaitEvent.Set();\n        }\n\n        #endregion\n\n        #region Writing\n\n        /// <summary>\n        /// Prevents write operations from deadlocking by throwing a TimeoutException if the WriteWaitEvent is not available within <see cref=\"ReadWriteTimeout\"/> milliseconds\n        /// </summary>\n        private void WriteWait()\n        {\n            if (!WriteWaitEvent.WaitOne(ReadWriteTimeout))\n                throw new TimeoutException(\"The write operation timed out waiting for the write lock WaitEvent. Check your usage of AcquireWriteLock/ReleaseWriteLock and AcquireReadLock/ReleaseReadLock.\");\n        }\n\n        /// <summary>\n        /// Writes an instance of <typeparamref name=\"T\"/> into the buffer\n        /// </summary>\n        /// <typeparam name=\"T\">A structure type</typeparam>\n        /// <param name=\"data\">A reference to an instance of <typeparamref name=\"T\"/> to be written</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to write to.</param>\n        protected override void Write<T>(ref T data, long bufferPosition = 0)\n        {\n            WriteWait();\n            base.Write<T>(ref data, bufferPosition);\n        }\n\n        /// <summary>\n        /// Writes an array of <typeparamref name=\"T\"/> into the buffer\n        /// </summary>\n        /// <typeparam name=\"T\">A structure type</typeparam>\n        /// <param name=\"buffer\">An array of <typeparamref name=\"T\"/> to be written. The length of this array controls the number of elements to be written.</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to write to.</param>\n        protected override void Write<T>(T[] buffer, long bufferPosition = 0)\n        {\n            WriteWait();\n            base.Write<T>(buffer, bufferPosition);\n        }\n\n        /// <summary>\n        /// Writes <paramref name=\"length\"/> bytes from the <paramref name=\"ptr\"/> into the shared memory buffer.\n        /// </summary>\n        /// <param name=\"ptr\">A managed pointer to the memory location to be copied into the buffer</param>\n        /// <param name=\"length\">The number of bytes to be copied</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to write to.</param>\n        protected override void Write(IntPtr ptr, int length, long bufferPosition = 0)\n        {\n            WriteWait();\n            base.Write(ptr, length, bufferPosition);\n        }\n\n        /// <summary>\n        /// Prepares an IntPtr to the buffer position and calls <paramref name=\"writeFunc\"/> to perform the writing.\n        /// </summary>\n        /// <param name=\"writeFunc\">A function used to write to the buffer. The IntPtr parameter is a pointer to the buffer offset by <paramref name=\"bufferPosition\"/>.</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region to start writing from.</param>\n        protected override void Write(Action<IntPtr> writeFunc, long bufferPosition = 0)\n        {\n            WriteWait();\n            base.Write(writeFunc, bufferPosition);\n        }\n\n        #endregion\n\n        #region Reading\n\n        /// <summary>\n        /// Prevents read operations from deadlocking by throwing a TimeoutException if the ReadWaitEvent is not available within <see cref=\"ReadWriteTimeout\"/> milliseconds\n        /// </summary>\n        private void ReadWait()\n        {\n            if (!ReadWaitEvent.WaitOne(ReadWriteTimeout))\n                throw new TimeoutException(\"The read operation timed out waiting for the read lock WaitEvent. Check your usage of AcquireWriteLock/ReleaseWriteLock and AcquireReadLock/ReleaseReadLock.\");\n        }\n\n        /// <summary>\n        /// Reads an instance of <typeparamref name=\"T\"/> from the buffer\n        /// </summary>\n        /// <typeparam name=\"T\">A structure type</typeparam>\n        /// <param name=\"data\">Output parameter that will contain the value read from the buffer</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to read from.</param>\n        protected override void Read<T>(out T data, long bufferPosition = 0)\n        {\n            ReadWait();\n            base.Read<T>(out data, bufferPosition);\n        }\n\n        /// <summary>\n        /// Reads an array of <typeparamref name=\"T\"/> from the buffer\n        /// </summary>\n        /// <typeparam name=\"T\">A structure type</typeparam>\n        /// <param name=\"buffer\">Array that will contain the values read from the buffer. The length of this array controls the number of elements to read.</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to read from.</param>\n        protected override void Read<T>(T[] buffer, long bufferPosition = 0)\n        {\n            ReadWait();\n            base.Read<T>(buffer, bufferPosition);\n        }\n\n        /// <summary>\n        /// Reads <paramref name=\"length\"/> bytes into the memory location <paramref name=\"destination\"/> from the shared memory buffer.\n        /// </summary>\n        /// <param name=\"destination\">A managed pointer to the memory location to copy data into from the buffer</param>\n        /// <param name=\"length\">The number of bytes to be copied</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to read from.</param>\n        protected override void Read(IntPtr destination, int length, long bufferPosition = 0)\n        {\n            ReadWait();\n            base.Read(destination, length, bufferPosition);\n        }\n\n        /// <summary>\n        /// Prepares an IntPtr to the buffer position and calls <paramref name=\"readFunc\"/> to perform the reading.\n        /// </summary>\n        /// <param name=\"readFunc\">A function used to read from the buffer. The IntPtr parameter is a pointer to the buffer offset by <paramref name=\"bufferPosition\"/>.</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to read from.</param>\n        protected override void Read(Action<IntPtr> readFunc, long bufferPosition = 0)\n        {\n            ReadWait();\n            base.Read(readFunc, bufferPosition);\n        }\n\n        #endregion\n\n        #region IDisposable\n\n        /// <summary>\n        /// IDisposable pattern\n        /// </summary>\n        /// <param name=\"disposeManagedResources\">true to release managed resources</param>\n        protected override void Dispose(bool disposeManagedResources)\n        {\n            if (disposeManagedResources)\n            {\n                (WriteWaitEvent as IDisposable).Dispose();\n                (ReadWaitEvent as IDisposable).Dispose();\n            }\n            base.Dispose(disposeManagedResources);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SharedMemory/CircularBuffer.cs",
    "content": "﻿// SharedMemory (File: SharedMemory\\CircularBuffer.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\n\nusing System;\nusing System.Collections.Generic;\nusing System.IO.MemoryMappedFiles;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Security.Permissions;\nusing System.Text;\nusing System.Threading;\n\nnamespace SharedMemory\n{\n    /// <summary>\n    /// A lock-free FIFO shared memory circular buffer (or ring buffer) utilising a <see cref=\"MemoryMappedFile\"/>.\n    /// </summary>\n#if NETFULL\n    [PermissionSet(SecurityAction.LinkDemand)]\n    [PermissionSet(SecurityAction.InheritanceDemand)]\n#endif\n    public unsafe class CircularBuffer : SharedBuffer\n    {\n        #region Public/Protected properties\n        \n        /// <summary>\n        /// The number of nodes within the circular linked-list\n        /// </summary>\n        public int NodeCount { get; private set; }\n        \n        /// <summary>\n        /// The buffer size of each node\n        /// </summary>\n        public int NodeBufferSize { get; private set; }\n        \n        /// <summary>\n        /// Event signaled when data has been written if the reading index has caught up to the writing index\n        /// </summary>\n        protected EventWaitHandle DataExists { get; set; }\n\n        /// <summary>\n        /// Event signaled when a node becomes available after reading if the writing index has caught up to the reading index\n        /// </summary>\n        protected EventWaitHandle NodeAvailable { get; set; }\n\n        /// <summary>\n        /// The offset relative to <see cref=\"SharedBuffer.BufferStartPtr\"/> where the node header starts within the buffer region of the shared memory\n        /// </summary>\n        protected virtual long NodeHeaderOffset\n        {\n            get\n            {\n                return 0;\n            }\n        }\n            \n        /// <summary>\n        /// Where the linked-list nodes are located within the buffer\n        /// </summary>\n        protected virtual long NodeOffset\n        {\n            get\n            {\n                return NodeHeaderOffset + Marshal.SizeOf(typeof(NodeHeader));\n            }\n        }\n\n        /// <summary>\n        /// Where the list of buffers are located within the shared memory\n        /// </summary>\n        protected virtual long NodeBufferOffset\n        {\n            get\n            {\n                return NodeOffset + (Marshal.SizeOf(typeof(Node)) * NodeCount);\n            }\n        }\n\n        /// <summary>\n        /// Provide direct access to the Node[] memory\n        /// </summary>\n        /// <param name=\"i\"></param>\n        /// <returns></returns>\n        protected virtual Node* this[int i]\n        {\n            get\n            {\n                if (i < 0 || i >= NodeCount)\n                    throw new ArgumentOutOfRangeException();\n\n                return ((Node*)(BufferStartPtr + NodeOffset)) + i;\n            }\n        }\n\n        #endregion\n\n        #region Private field members\n\n        private NodeHeader* _nodeHeader = null;\n        \n        #endregion\n\n        #region Structures\n\n        /// <summary>\n        /// Provides cursors for the circular buffer along with dimensions\n        /// </summary>\n        /// <remarks>This structure is the same size on 32-bit and 64-bit architectures.</remarks>\n        [StructLayout(LayoutKind.Sequential)]\n        public struct NodeHeader\n        {\n            /// <summary>\n            /// The index of the first unreadable node\n            /// </summary>\n            public volatile int ReadEnd;\n            /// <summary>\n            /// The index of the next readable node\n            /// </summary>\n            public volatile int ReadStart;\n\n            /// <summary>\n            /// The index of the first unwritable node\n            /// </summary>\n            public volatile int WriteEnd;\n            /// <summary>\n            /// The index of the next writable node\n            /// </summary>\n            public volatile int WriteStart;\n\n            /// <summary>\n            /// The number of nodes within the buffer\n            /// </summary>\n            public int NodeCount;\n\n            /// <summary>\n            /// The size of the buffer for each node\n            /// </summary>\n            public int NodeBufferSize;\n        }\n\n        /// <summary>\n        /// Represents a node within the buffer's circular linked list\n        /// </summary>\n        /// <remarks>This structure is the same size on 32-bit and 64-bit architectures.</remarks>\n        [StructLayout(LayoutKind.Sequential)]\n        public struct Node\n        {\n            /// <summary>\n            /// The previous node.\n            /// </summary>\n            public int Next;\n\n            /// <summary>\n            /// The next node.\n            /// </summary>\n            public int Prev;\n\n            /// <summary>\n            /// A flag used while returning a node for writing after having been read.\n            /// </summary>\n            public volatile int DoneRead;\n\n            /// <summary>\n            /// A flag used while posting a node for reading after writing is completed.\n            /// </summary>\n            public volatile int DoneWrite;\n\n            /// <summary>\n            /// Represents the offset relative to <see cref=\"SharedBuffer.BufferStartPtr\"/> where the data for this node can be found.\n            /// </summary>\n            public long Offset;\n            \n            /// <summary>\n            /// Represents the index of the current node.\n            /// </summary>\n            public int Index;\n\n            /// <summary>\n            /// Holds the number of bytes written into this node.\n            /// </summary>\n            public int AmountWritten;\n        }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        /// Creates and opens a new <see cref=\"CircularBuffer\"/> instance with the specified name, node count and buffer size per node.\n        /// </summary>\n        /// <param name=\"name\">The name of the shared memory to be created</param>\n        /// <param name=\"nodeCount\">The number of nodes within the circular linked-list (minimum of 2)</param>\n        /// <param name=\"nodeBufferSize\">The buffer size per node in bytes. The total shared memory size will be <code>Marshal.SizeOf(SharedMemory.SharedHeader) + Marshal.SizeOf(CircularBuffer.NodeHeader) + (Marshal.SizeOf(CircularBuffer.Node) * nodeCount) + (bufferSize * nodeCount)</code></param>\n        /// <remarks>\n        /// <para>The maximum total shared memory size is dependent upon the system and current memory fragmentation.</para>\n        /// <para>The shared memory layout on 32-bit and 64-bit architectures is:<br />\n        /// <code>\n        /// |       Header       |   NodeHeader  | Node[0] | ... | Node[N-1] | buffer[0] | ... | buffer[N-1] |<br />\n        /// |      16-bytes      |    24-bytes   |       32-bytes * N        |     NodeBufferSize * N        |<br />\n        ///                      |------------------------------BufferSize-----------------------------------|<br />\n        /// |-----------------------------------------SharedMemorySize---------------------------------------|\n        /// </code>\n        /// </para>\n        /// </remarks>\n        public CircularBuffer(string name, int nodeCount, int nodeBufferSize)\n            : this(name, nodeCount, nodeBufferSize, true)\n        {\n            Open();\n        }\n\n        /// <summary>\n        /// Opens an existing <see cref=\"CircularBuffer\"/> with the specified name.\n        /// </summary>\n        /// <param name=\"name\">The name of an existing <see cref=\"CircularBuffer\"/> previously created with <see cref=\"SharedBuffer.IsOwnerOfSharedMemory\"/>=true</param>\n        public CircularBuffer(string name)\n            : this(name, 0, 0, false)\n        {\n            Open();\n        }\n\n        private CircularBuffer(string name, int nodeCount, int nodeBufferSize, bool ownsSharedMemory)\n            : base(name, Marshal.SizeOf(typeof(NodeHeader)) + (Marshal.SizeOf(typeof(Node)) * nodeCount) + (nodeCount * (long)nodeBufferSize), ownsSharedMemory)\n        {\n            #region Argument validation\n            if (ownsSharedMemory && nodeCount < 2)\n                throw new ArgumentOutOfRangeException(\"nodeCount\", nodeCount, \"The node count must be a minimum of 2.\");\n#if DEBUG\n            else if (!ownsSharedMemory && (nodeCount != 0 || nodeBufferSize > 0))\n                System.Diagnostics.Debug.Write(\"Node count and nodeBufferSize are ignored when opening an existing shared memory circular buffer.\", \"Warning\");\n#endif\n            #endregion\n\n            if (IsOwnerOfSharedMemory)\n            {\n                NodeCount = nodeCount;\n                NodeBufferSize = nodeBufferSize;\n            }\n        }\n\n        #endregion\n\n        #region Open / Close\n\n        /// <summary>\n        /// Attempts to create the <see cref=\"EventWaitHandle\"/> handles and initialise the node header and buffers.\n        /// </summary>\n        /// <returns>True if the events and nodes were initialised successfully.</returns>\n        protected override bool DoOpen()\n        {\n            // Create signal events\n            DataExists = new EventWaitHandle(false, EventResetMode.AutoReset, Name + \"_evt_dataexists\");\n            NodeAvailable = new EventWaitHandle(false, EventResetMode.AutoReset, Name + \"_evt_nodeavail\");\n\n            if (IsOwnerOfSharedMemory)\n            {\n                // Retrieve pointer to node header\n                _nodeHeader = (NodeHeader*)(BufferStartPtr + NodeHeaderOffset);\n\n                // Initialise the node header\n                InitialiseNodeHeader();\n\n                // Initialise nodes entries\n                InitialiseLinkedListNodes();\n            }\n            else\n            {\n                // Load the NodeHeader\n                _nodeHeader = (NodeHeader*)(BufferStartPtr + NodeHeaderOffset);\n                NodeCount = _nodeHeader->NodeCount;\n                NodeBufferSize = _nodeHeader->NodeBufferSize;\n            }\n\n            return true;\n        }\n\n        /// <summary>\n        /// Initialises the node header within the shared memory. Only applicable if <see cref=\"SharedBuffer.IsOwnerOfSharedMemory\"/> is true.\n        /// </summary>\n        private void InitialiseNodeHeader()\n        {\n            if (!IsOwnerOfSharedMemory)\n                return;\n\n            NodeHeader header = new NodeHeader();\n            header.ReadStart = 0;\n            header.ReadEnd = 0;\n            header.WriteEnd = 0;\n            header.WriteStart = 0;\n            header.NodeBufferSize = NodeBufferSize;\n            header.NodeCount = NodeCount;\n            base.Write<NodeHeader>(ref header, NodeHeaderOffset);\n        }\n\n        /// <summary>\n        /// Initialise the nodes of the circular linked-list. Only applicable if <see cref=\"SharedBuffer.IsOwnerOfSharedMemory\"/> is true.\n        /// </summary>\n        private void InitialiseLinkedListNodes()\n        {\n            if (!IsOwnerOfSharedMemory)\n                return;\n\n            int N = 0;\n\n            Node[] nodes = new Node[NodeCount];\n\n            // First node\n            nodes[N].Next = 1;\n            nodes[N].Prev = NodeCount - 1;\n            nodes[N].Offset = NodeBufferOffset;\n            nodes[N].Index = N;\n            // Middle nodes\n            for (N = 1; N < NodeCount - 1; N++)\n            {\n                nodes[N].Next = N + 1;\n                nodes[N].Prev = N - 1;\n                nodes[N].Offset = NodeBufferOffset + (NodeBufferSize * N);\n                nodes[N].Index = N;\n            }\n            // Last node\n            nodes[N].Next = 0;\n            nodes[N].Prev = NodeCount - 2;\n            nodes[N].Offset = NodeBufferOffset + (NodeBufferSize * N);\n            nodes[N].Index = N;\n\n            // Write the nodes to the shared memory\n            base.WriteArray<Node>(nodes, 0, nodes.Length, NodeOffset);\n        }\n\n        /// <summary>\n        /// Closes the events. The shared memory could still be open within one or more other instances.\n        /// </summary>\n        protected override void DoClose()\n        {\n            if (DataExists != null)\n            {\n                (DataExists as IDisposable).Dispose();\n                DataExists = null;\n                (NodeAvailable as IDisposable).Dispose();\n                NodeAvailable = null;\n            }\n\n            _nodeHeader = null;\n        }\n\n        #endregion\n\n        #region Node Writing\n\n        /// <summary>\n        /// Attempts to reserve a node from the linked-list for writing with the specified timeout.\n        /// </summary>\n        /// <param name=\"timeout\">The number of milliseconds to wait if a node is not immediately available for writing.</param>\n        /// <returns>An unsafe pointer to the node if successful, otherwise null</returns>\n        protected virtual Node* GetNodeForWriting(int timeout)\n        {\n            for (; ; )\n            {\n                int blockIndex = _nodeHeader->WriteStart;\n                Node* node = this[blockIndex];\n                if (node->Next == _nodeHeader->ReadEnd)\n                {\n                    // No room is available, wait for room to become available\n                    if (NodeAvailable.WaitOne(timeout))\n                        continue;\n\n                    // Timeout\n                    return null;\n                }\n\n#pragma warning disable 0420 // ignore ref to volatile warning - Interlocked API\n                if (Interlocked.CompareExchange(ref _nodeHeader->WriteStart, node->Next, blockIndex) == blockIndex)\n                    return node;\n#pragma warning restore 0420\n\n                // Another thread has already acquired this node for writing, try again.\n                continue;\n            }\n        }\n\n        /// <summary>\n        /// Makes a node available for reading after writing is complete\n        /// </summary>\n        /// <param name=\"node\">An unsafe pointer to the node to return</param>\n        protected virtual void PostNode(Node* node)\n        {\n            // Set the write flag for this node (the node is reserved so no need for locks)\n            node->DoneWrite = 1;\n\n            // Move the write pointer as far forward as we can\n            // always starting from WriteEnd to make all contiguous\n            // completed nodes available for reading.\n            for (; ; )\n            {\n                int blockIndex = _nodeHeader->WriteEnd;\n                node = this[blockIndex];\n#pragma warning disable 0420 // ignore ref to volatile warning - Interlocked API\n                if (Interlocked.CompareExchange(ref node->DoneWrite, 0, 1) != 1)\n                {\n                    // If we get here then another thread either another thread\n                    // has already moved the write index or we have moved forward \n                    // as far as we can\n                    return;\n                }\n\n                // Move the pointer one forward\n                Interlocked.CompareExchange(ref _nodeHeader->WriteEnd, node->Next, blockIndex);\n#pragma warning restore 0420\n\n                // Signal the \"data exists\" event if read threads are waiting\n                if (blockIndex == _nodeHeader->ReadStart)\n                    DataExists.Set();\n            }\n        }\n\n        /// <summary>\n        /// Writes the byte array buffer to the next available node for writing\n        /// </summary>\n        /// <param name=\"source\">Reference to the buffer to write</param>\n        /// <param name=\"startIndex\">The index within the buffer to start writing from</param>\n        /// <param name=\"timeout\">The maximum number of milliseconds to wait for a node to become available for writing (default 1000ms)</param>\n        /// <returns>The number of bytes written</returns>\n        /// <remarks>The maximum number of bytes that can be written is the minimum of the length of <paramref name=\"source\"/> and <see cref=\"NodeBufferSize\"/>.</remarks>\n        public virtual int Write(byte[] source, int startIndex = 0, int timeout = 1000)\n        {\n            // Grab a node for writing\n            Node* node = GetNodeForWriting(timeout);\n            if (node == null) return 0;\n\n            // Copy the data\n            int amount = Math.Min(source.Length - startIndex, NodeBufferSize);\n            \n            Marshal.Copy(source, startIndex, new IntPtr(BufferStartPtr + node->Offset), amount);\n            node->AmountWritten = amount;\n            \n\n            // Writing is complete, make readable\n            PostNode(node);\n\n            return amount;\n        }\n\n        /// <summary>\n        /// Writes the structure array buffer to the next available node for writing\n        /// </summary>\n        /// <param name=\"source\">Reference to the buffer to write</param>\n        /// <param name=\"startIndex\">The index within the buffer to start writing from</param>\n        /// <param name=\"timeout\">The maximum number of milliseconds to wait for a node to become available for writing (default 1000ms)</param>\n        /// <returns>The number of elements written</returns>\n        /// <remarks>The maximum number of elements that can be written is the minimum of the length of <paramref name=\"source\"/> subtracted by <paramref name=\"startIndex\"/> and <see cref=\"NodeBufferSize\"/> divided by <code>FastStructure.SizeOf&gt;T&lt;()</code>.</remarks>        \n        public virtual int Write<T>(T[] source, int startIndex = 0, int timeout = 1000)\n            where T : struct\n        {\n            // Grab a node for writing\n            Node* node = GetNodeForWriting(timeout);\n            if (node == null) return 0;\n\n            // Write the data using the FastStructure class (much faster than the MemoryMappedViewAccessor WriteArray<T> method)\n            int count = Math.Min(source.Length - startIndex, NodeBufferSize / FastStructure.SizeOf<T>());\n            base.WriteArray<T>(source, startIndex, count, node->Offset);\n            node->AmountWritten = count * FastStructure.SizeOf<T>();\n\n            // Writing is complete, make node readable\n            PostNode(node);\n\n            return count;\n        }\n\n        /// <summary>\n        /// Writes the structure to the next available node for writing\n        /// </summary>\n        /// <typeparam name=\"T\">The structure type to be written</typeparam>\n        /// <param name=\"source\">The structure to be written</param>\n        /// <param name=\"timeout\">The maximum number of milliseconds to wait for a node to become available for writing (default 1000ms)</param>\n        /// <returns>The number of bytes written - larger than 0 if successful</returns>\n        /// <exception cref=\"ArgumentOutOfRangeException\">If the size of the <typeparamref name=\"T\"/> structure is larger than <see cref=\"NodeBufferSize\"/>.</exception>\n        public virtual int Write<T>(ref T source, int timeout = 1000)\n            where T : struct\n        {\n            int structSize = Marshal.SizeOf(typeof(T));\n            if (structSize > NodeBufferSize)\n                throw new ArgumentOutOfRangeException(\"T\", \"The size of structure \" + typeof(T).Name + \" is larger than NodeBufferSize\");\n\n            // Attempt to retrieve a node for writing\n            Node* node = GetNodeForWriting(timeout);\n            if (node == null) return 0;\n\n            // Copy the data using the MemoryMappedViewAccessor\n            base.Write<T>(ref source, node->Offset);\n            node->AmountWritten = structSize;\n\n            // Return the node for further writing\n            PostNode(node);\n\n            return structSize;\n        }\n\n        /// <summary>\n        /// Writes <paramref name=\"length\"/> bytes from <paramref name=\"source\"/> to the next available node for writing\n        /// </summary>\n        /// <param name=\"source\">Pointer to the buffer to copy</param>\n        /// <param name=\"timeout\">The maximum number of milliseconds to wait for a node to become available (default 1000ms)</param>\n        /// <param name=\"length\">The number of bytes to attempt to write</param>\n        /// <returns>The number of bytes written</returns>\n        /// <remarks>The maximum number of bytes that can be written is the minimum of <paramref name=\"length\"/> and <see cref=\"NodeBufferSize\"/>.</remarks>        \n        public virtual int Write(IntPtr source, int length, int timeout = 1000)\n        {\n            // Grab a node for writing\n            Node* node = GetNodeForWriting(timeout);\n            if (node == null) return 0;\n\n            // Copy the data\n            int amount = Math.Min(length, NodeBufferSize);\n            base.Write(source, amount, node->Offset);\n            node->AmountWritten = amount;\n\n            // Writing is complete, make readable\n            PostNode(node);\n\n            return amount;\n        }\n\n        /// <summary>\n        /// Reserves a node for writing and then calls the provided <paramref name=\"writeFunc\"/> to perform the write operation.\n        /// </summary>\n        /// <param name=\"writeFunc\">A function to used to write to the node's buffer. The first parameter is a pointer to the node's buffer. \n        /// The provided function should return the number of bytes written.</param>\n        /// <param name=\"timeout\">The maximum number of milliseconds to wait for a node to become available for writing (default 1000ms)</param>\n        /// <returns>The number of bytes written</returns>\n        public virtual int Write(Func<IntPtr, int> writeFunc, int timeout = 1000)\n        {\n            // Grab a node for writing\n            Node* node = GetNodeForWriting(timeout);\n            if (node == null) return 0;\n\n            int amount = 0;\n            try\n            {\n                // Pass destination IntPtr to custom write function\n                amount = writeFunc(new IntPtr(BufferStartPtr + node->Offset));\n                node->AmountWritten = amount;\n            }\n            finally\n            {\n                // Writing is complete, make readable\n                PostNode(node);\n            }\n\n            return amount;\n        }\n\n        #endregion\n\n        #region Node Reading\n\n        /// <summary>\n        /// Returns a copy of the shared memory header\n        /// </summary>\n        public NodeHeader ReadNodeHeader()\n        {\n            return (NodeHeader)Marshal.PtrToStructure(new IntPtr(_nodeHeader), typeof(NodeHeader));\n        }\n\n        /// <summary>\n        /// Attempts to reserve a node from the linked-list for reading with the specified timeout\n        /// </summary>\n        /// <param name=\"timeout\">The number of milliseconds to wait if a node is not immediately available for reading.</param>\n        /// <returns>An unsafe pointer to the node if successful, otherwise null</returns>\n        protected virtual Node* GetNodeForReading(int timeout)\n        {\n            for (; ; )\n            {\n                int blockIndex = _nodeHeader->ReadStart;\n                Node* node = this[blockIndex];\n                if (blockIndex == _nodeHeader->WriteEnd)\n                {\n                    // No data is available, wait for it\n                    if (DataExists.WaitOne(timeout))\n                        continue;\n\n                    // Timeout\n                    return null;\n                }\n\n#pragma warning disable 0420 // ignore ref to volatile warning - Interlocked API\n                if (Interlocked.CompareExchange(ref _nodeHeader->ReadStart, node->Next, blockIndex) == blockIndex)\n                    return node;\n#pragma warning restore 0420\n\n                // Another thread has already acquired this node for reading, try again\n                continue;\n            }\n        }\n\n        /// <summary>\n        /// Returns a node to the available list of nodes for writing.\n        /// </summary>\n        /// <param name=\"node\">An unsafe pointer to the node to be returned</param>\n        protected virtual void ReturnNode(Node* node)\n        {\n            // Set the finished reading flag for this node (the node is reserved so no need for locks)\n            node->DoneRead = 1;\n\n            // Keep it clean and reset AmountWritten to prepare it for next Write\n            node->AmountWritten = 0;\n\n            // Move the read pointer forward as far as possible\n            // always starting from ReadEnd to make all contiguous\n            // read nodes available for writing.\n            for (; ; )\n            {\n                int blockIndex = _nodeHeader->ReadEnd;\n                node = this[blockIndex];\n#pragma warning disable 0420 // ignore ref to volatile warning - Interlocked API\n                if (Interlocked.CompareExchange(ref node->DoneRead, 0, 1) != 1)\n                {\n                    // If we get here then another read thread has already moved the pointer\n                    // or we have moved ReadEnd as far forward as we can\n                    return;\n                }\n\n                // Move the pointer forward one node\n                Interlocked.CompareExchange(ref _nodeHeader->ReadEnd, node->Next, blockIndex);\n#pragma warning restore 0420\n\n               // If a writer thread is waiting on \"node available\" signal the event\n                if (node->Prev == _nodeHeader->WriteStart)\n                        NodeAvailable.Set();\n            }\n        }\n\n        /// <summary>\n        /// Reads the next available node for reading into the specified byte array\n        /// </summary>\n        /// <param name=\"destination\">Reference to the buffer</param>\n        /// <param name=\"startIndex\">The index within the buffer to start writing from</param>\n        /// <param name=\"timeout\">The maximum number of milliseconds to wait for a node to become available for reading (default 1000ms)</param>\n        /// <returns>The number of bytes read</returns>\n        /// <remarks>The maximum number of bytes that can be read is the minimum of the length of <paramref name=\"destination\"/> subtracted by <paramref name=\"startIndex\"/> and <see cref=\"NodeBufferSize\"/>.</remarks>\n        public virtual int Read(byte[] destination, int startIndex = 0, int timeout = 1000)\n        {\n            Node* node = GetNodeForReading(timeout);\n            if (node == null) return 0;\n\n            //int amount = Math.Min(buffer.Length, NodeBufferSize);\n            int amount = Math.Min(destination.Length - startIndex, node->AmountWritten);\n\n            // Copy the data\n            Marshal.Copy(new IntPtr(BufferStartPtr + node->Offset), destination, startIndex, amount);\n\n            // Return the node for further writing\n            ReturnNode(node);\n\n            return amount;\n        }\n\n        /// <summary>\n        /// Reads the next available node for reading into the specified structure array\n        /// </summary>\n        /// <typeparam name=\"T\">The structure type to be read</typeparam>\n        /// <param name=\"destination\">Reference to the buffer</param>\n        /// <param name=\"startIndex\">The index within the destination to start writing to.</param>\n        /// <param name=\"timeout\">The maximum number of milliseconds to wait for a node to become available for reading (default 1000ms)</param>\n        /// <returns>The number of elements read into destination</returns>\n        /// <remarks>The maximum number of elements that can be read is the minimum of the length of <paramref name=\"destination\"/> subtracted by <paramref name=\"startIndex\"/> and <see cref=\"Node.AmountWritten\"/> divided by <code>FastStructure.SizeOf&gt;T&lt;()</code>.</remarks>\n        public virtual int Read<T>(T[] destination, int startIndex = 0, int timeout = 1000)\n            where T : struct\n        {\n            Node* node = GetNodeForReading(timeout);\n            if (node == null) return 0;\n\n            // Copy the data using the FastStructure class (much faster than the MemoryMappedViewAccessor ReadArray<T> method)\n            int count = Math.Min(destination.Length - startIndex, node->AmountWritten / FastStructure.SizeOf<T>());\n            base.ReadArray<T>(destination, startIndex, count, node->Offset);\n\n            // Return the node for further writing\n            ReturnNode(node);\n\n            return count;\n        }\n\n        /// <summary>\n        /// Reads the next available node for reading into the a structure\n        /// </summary>\n        /// <typeparam name=\"T\">The structure type to be read</typeparam>\n        /// <param name=\"destination\">The resulting structure if successful otherwise default(T)</param>\n        /// <param name=\"timeout\">The maximum number of milliseconds to wait for a node to become available for reading (default 1000ms)</param>\n        /// <returns>The number of bytes read</returns>\n        /// <exception cref=\"ArgumentOutOfRangeException\">If the size of <typeparamref name=\"T\"/> is larger than <see cref=\"NodeBufferSize\"/>.</exception>\n        public virtual int Read<T>(out T destination, int timeout = 1000)\n            where T: struct\n        {\n            int structSize = Marshal.SizeOf(typeof(T));\n            if (structSize > NodeBufferSize)\n                throw new ArgumentOutOfRangeException(\"T\", \"The size of structure \" + typeof(T).Name + \" is larger than NodeBufferSize\");\n\n            // Attempt to retrieve a node\n            Node* node = GetNodeForReading(timeout);\n            if (node == null)\n            {\n                destination = default(T);\n                return 0;\n            }\n\n            // Copy the data using the MemoryMappedViewAccessor\n            base.Read<T>(out destination, node->Offset);\n\n            // Return the node for further writing\n            ReturnNode(node);\n\n            return structSize;\n        }\n\n        /// <summary>\n        /// Reads the next available node for reading into the specified memory location with the specified length\n        /// </summary>\n        /// <param name=\"destination\">Pointer to the buffer</param>\n        /// <param name=\"length\">The maximum length of <paramref name=\"destination\"/></param>\n        /// <param name=\"timeout\">The maximum number of milliseconds to wait for a node to become available for reading (default 1000ms)</param>\n        /// <returns>The number of bytes read</returns>\n        /// <remarks>The maximum number of bytes that can be read is the minimum of the <paramref name=\"length\"/> and <see cref=\"Node.AmountWritten\"/>.</remarks>\n        public virtual int Read(IntPtr destination, int length, int timeout = 1000)\n        {\n            Node* node = GetNodeForReading(timeout);\n            if (node == null) return 0;\n\n            //int amount = Math.Min(length, NodeBufferSize);\n            int amount = Math.Min(length, node->AmountWritten);\n\n            // Copy the data\n            base.Read(destination, amount, node->Offset);\n\n            // Return node for further writing\n            ReturnNode(node);\n\n            return amount;\n        }\n\n        /// <summary>\n        /// Reserves a node for reading and then calls the provided <paramref name=\"readFunc\"/> to perform the read operation.\n        /// </summary>\n        /// <param name=\"readFunc\">A function used to read from the node's buffer. The first parameter is a pointer to the node's buffer. \n        /// The provided function should return the number of bytes read.</param>\n        /// <param name=\"timeout\">The maximum number of milliseconds to wait for a node to become available for reading (default 1000ms)</param>\n        /// <returns>The number of bytes read</returns>\n        public virtual int Read(Func<IntPtr, int> readFunc, int timeout = 1000)\n        {\n            Node* node = GetNodeForReading(timeout);\n            if (node == null) return 0;\n\n            int amount = 0;\n            try\n            {\n                // Pass pointer to buffer directly to custom read function\n                amount = readFunc(new IntPtr(BufferStartPtr + node->Offset));\n            }\n            finally\n            {\n                // Return the node for further writing\n                ReturnNode(node);\n            }\n            return amount;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SharedMemory/FastStructure.cs",
    "content": "﻿// SharedMemory (File: SharedMemory\\FastStructure.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\nusing System;\nusing System.Linq;\nusing System.Reflection;\nusing System.Reflection.Emit;\n\nnamespace SharedMemory\n{\n    /// <summary>\n    /// Provides fast reading and writing of generic structures to a memory location using IL emitted functions.\n    /// </summary>\n    public static class FastStructure\n    {\n        /// <summary>\n        /// Retrieve a pointer to the passed generic structure type. This is achieved by emitting a <see cref=\"DynamicMethod\"/> to retrieve a pointer to the structure.\n        /// </summary>\n        /// <typeparam name=\"T\"></typeparam>\n        /// <param name=\"structure\"></param>\n        /// <returns>A pointer to the provided structure in memory.</returns>\n        /// <see cref=\"FastStructure{T}.GetPtr\"/>\n        public static unsafe void* GetPtr<T>(ref T structure)\n            where T : struct\n        {\n            return FastStructure<T>.GetPtr(ref structure);\n        }\n\n        /// <summary>\n        /// Loads the generic value type <typeparamref name=\"T\"/> from a pointer. This is achieved by emitting a <see cref=\"DynamicMethod\"/> that returns the value in the memory location as a <typeparamref name=\"T\"/>.\n        /// <para>The equivalent non-generic C# code:</para>\n        /// <code>\n        /// unsafe MyStruct ReadFromPointer(byte* pointer)\n        /// {\n        ///     return *(MyStruct*)pointer;\n        /// }\n        /// </code>\n        /// </summary>\n        /// <typeparam name=\"T\">Any value/structure type</typeparam>\n        /// <param name=\"pointer\">Unsafe pointer to memory to load the value from</param>\n        /// <returns>The newly loaded value</returns>\n        public static unsafe T PtrToStructure<T>(IntPtr pointer)\n            where T : struct\n        {\n            return FastStructure<T>.PtrToStructure(pointer);\n        }\n\n        /// <summary>\n        /// Writes the generic value type <typeparamref name=\"T\"/> to the location specified by a pointer. This is achieved by emitting a <see cref=\"DynamicMethod\"/> that copies the value from the referenced structure into the specified memory location.\n        /// <para>There is no exact equivalent possible in C#, the closest possible (generates the same IL) is the following code:</para>\n        /// <code>\n        /// unsafe void WriteToPointer(ref SharedHeader dest, ref SharedHeader src)\n        /// {\n        ///     dest = src;\n        /// }\n        /// </code>\n        /// </summary>\n        /// <typeparam name=\"T\"></typeparam>\n        /// <param name=\"pointer\"></param>\n        /// <param name=\"structure\"></param>\n        public static unsafe void StructureToPtr<T>(ref T structure, IntPtr pointer)\n            where T : struct\n        {\n            FastStructure<T>.StructureToPtr(ref structure, pointer);\n        }\n\n        /// <summary>\n        /// Copy bytes of structure into the existing buffer at index\n        /// </summary>\n        /// <typeparam name=\"T\"></typeparam>\n        /// <param name=\"structure\"></param>\n        /// <param name=\"buffer\"></param>\n        /// <param name=\"startIndex\"></param>\n        /// <returns></returns>\n        public static unsafe void CopyTo<T>(ref T structure, byte[] buffer, int startIndex = 0)\n            where T : struct\n        {\n            if (buffer == null)\n                throw new ArgumentNullException(\"buffer\");\n            if (startIndex > buffer.Length || startIndex < 0)\n                throw new ArgumentOutOfRangeException(\"startIndex\");\n\n            fixed (byte* p = &buffer[startIndex])\n            {\n                StructureToPtr<T>(ref structure, new IntPtr(p));\n            }\n        }\n\n        /// <summary>\n        /// Return a byte[] for the provided structure\n        /// </summary>\n        /// <typeparam name=\"T\"></typeparam>\n        /// <param name=\"structure\"></param>\n        /// <returns></returns>\n        public static unsafe byte[] ToBytes<T>(ref T structure)\n            where T : struct\n        {\n            byte[] result = new byte[FastStructure<T>.Size];\n            fixed (byte* p = &result[0])\n            {\n                StructureToPtr<T>(ref structure, new IntPtr(p));\n                return result;\n            }\n        }\n\n        /// <summary>\n        /// Read structure from the provided byte array\n        /// </summary>\n        /// <typeparam name=\"T\"></typeparam>\n        /// <param name=\"buffer\"></param>\n        /// <param name=\"startIndex\"></param>\n        /// <returns></returns>\n        public static unsafe T FromBytes<T>(byte[] buffer, int startIndex = 0)\n            where T : struct\n        {\n            if (buffer == null)\n                throw new ArgumentNullException(\"buffer\");\n            if (startIndex > buffer.Length || startIndex < 0)\n                throw new ArgumentOutOfRangeException(\"startIndex\");\n\n            fixed (byte* p = &buffer[startIndex])\n            {\n                return PtrToStructure<T>(new IntPtr(p));\n            }\n        }\n\n        /// <summary>\n        /// Retrieve the cached size of a structure\n        /// </summary>\n        /// <typeparam name=\"T\"></typeparam>\n        /// <returns></returns>\n        /// <remarks>Caches the size by type</remarks>\n        /// <see cref=\"FastStructure{T}.Size\"/>\n        public static int SizeOf<T>()\n            where T : struct\n        {\n            return FastStructure<T>.Size;\n        }\n\n        /// <summary>\n        /// Reads a number of elements from a memory location into the provided buffer starting at the specified index.\n        /// </summary>\n        /// <typeparam name=\"T\">The structure type</typeparam>\n        /// <param name=\"buffer\">The destination buffer.</param>\n        /// <param name=\"source\">The source memory location.</param>\n        /// <param name=\"index\">The start index within <paramref name=\"buffer\"/>.</param>\n        /// <param name=\"count\">The number of elements to read.</param>\n        public static unsafe void ReadArray<T>(T[] buffer, IntPtr source, int index, int count)\n            where T : struct\n        {\n            uint elementSize = (uint)SizeOf<T>();\n\n            if (buffer == null)\n                throw new ArgumentNullException(\"buffer\");\n            if (count < 0)\n                throw new ArgumentOutOfRangeException(\"count\");\n            if (index < 0)\n                throw new ArgumentOutOfRangeException(\"index\");\n            if (buffer.Length - index < count)\n                throw new ArgumentException(\"Invalid offset into array specified by index and count\");\n\n            void* ptr = source.ToPointer();\n            byte* p = (byte*)FastStructure.GetPtr<T>(ref buffer[0]);\n#if NETCORE\n            Buffer.MemoryCopy(ptr, p + (index * elementSize), elementSize * count, elementSize * count);\n#else\n            UnsafeNativeMethods.CopyMemoryPtr(p + (index * elementSize), ptr, (uint)(elementSize * count));\n#endif\n        }\n\n        /// <summary>\n        /// Reads a number of elements from a memory location into the provided buffer starting at the specified index.\n        /// </summary>\n        /// <param name=\"buffer\">The destination buffer.</param>\n        /// <param name=\"source\">The source memory location.</param>\n        /// <param name=\"index\">The start index within <paramref name=\"buffer\"/>.</param>\n        /// <param name=\"count\">The number of elements to read.</param>\n        public static unsafe void ReadBytes(byte[] buffer, IntPtr source, int index, int count)\n        {\n            uint elementSize = sizeof(byte);\n\n            if (buffer == null)\n                throw new ArgumentNullException(\"buffer\");\n            if (count < 0)\n                throw new ArgumentOutOfRangeException(\"count\");\n            if (index < 0)\n                throw new ArgumentOutOfRangeException(\"index\");\n            if (buffer.Length - index < count)\n                throw new ArgumentException(\"Invalid offset into array specified by index and count\");\n\n            void* ptr = source.ToPointer();\n\n            fixed (byte* p = &buffer[0])\n            {\n#if NETCORE\n                Buffer.MemoryCopy(ptr, p + (index * elementSize), elementSize * count, elementSize * count);\n#else\n                UnsafeNativeMethods.CopyMemoryPtr(p + (index * elementSize), ptr, (uint)(elementSize * count));\n#endif\n            }\n        }\n\n        /// <summary>\n        /// Writes a number of elements to a memory location from the provided buffer starting at the specified index.\n        /// </summary>\n        /// <typeparam name=\"T\">The structure type</typeparam>\n        /// <param name=\"destination\">The destination memory location.</param>\n        /// <param name=\"buffer\">The source buffer.</param>\n        /// <param name=\"index\">The start index within <paramref name=\"buffer\"/>.</param>\n        /// <param name=\"count\">The number of elements to write.</param>\n        public static unsafe void WriteArray<T>(IntPtr destination, T[] buffer, int index, int count)\n            where T : struct\n        {\n            uint elementSize = (uint)SizeOf<T>();\n\n            if (buffer == null)\n                throw new ArgumentNullException(\"buffer\");\n            if (count < 0)\n                throw new ArgumentOutOfRangeException(\"count\");\n            if (index < 0)\n                throw new ArgumentOutOfRangeException(\"index\");\n            if (buffer.Length - index < count)\n                throw new ArgumentException(\"Invalid offset into array specified by index and count\");\n\n            void* ptr = destination.ToPointer();\n            byte* p = (byte*)FastStructure.GetPtr<T>(ref buffer[0]);\n#if NETCORE\n            Buffer.MemoryCopy(p + (index * elementSize), ptr, elementSize * count, elementSize * count);\n#else\n            UnsafeNativeMethods.CopyMemoryPtr(ptr, p + (index * elementSize), (uint)(elementSize * count));\n#endif\n        }\n\n        /// <summary>\n        /// Writes a number of elements to a memory location from the provided buffer starting at the specified index.\n        /// </summary>\n        /// <param name=\"destination\">The destination memory location.</param>\n        /// <param name=\"buffer\">The source buffer.</param>\n        /// <param name=\"index\">The start index within <paramref name=\"buffer\"/>.</param>\n        /// <param name=\"count\">The number of elements to write.</param>\n        public static unsafe void WriteBytes(IntPtr destination, byte[] buffer, int index, int count)\n        {\n            uint elementSize = sizeof(byte);\n\n            if (buffer == null)\n                throw new ArgumentNullException(\"buffer\");\n            if (count < 0)\n                throw new ArgumentOutOfRangeException(\"count\");\n            if (index < 0)\n                throw new ArgumentOutOfRangeException(\"index\");\n            if (buffer.Length - index < count)\n                throw new ArgumentException(\"Invalid offset into array specified by index and count\");\n\n            void* ptr = destination.ToPointer();\n            fixed (byte* p = &buffer[0])\n            {\n#if NETCORE\n                Buffer.MemoryCopy(p + (index * elementSize), ptr, elementSize * count, elementSize * count);\n#else\n                UnsafeNativeMethods.CopyMemoryPtr(ptr, p + (index * elementSize), (uint)(elementSize * count));\n#endif\n            }\n        }\n    }\n\n    /// <summary>\n    /// Emits optimized IL for the reading and writing of structures to/from memory.\n    /// <para>For a 32-byte structure with 1 million iterations:</para>\n    /// <para>The <see cref=\"FastStructure{T}.PtrToStructure\"/> method performs approx. 20x faster than\n    /// <see cref=\"System.Runtime.InteropServices.Marshal.PtrToStructure(IntPtr, Type)\"/> (8ms vs 160ms), and about 1.6x slower than the non-generic equivalent (8ms vs 5ms)</para>\n    /// <para>The <see cref=\"FastStructure{T}.StructureToPtr\"/> method performs approx. 8x faster than \n    /// <see cref=\"System.Runtime.InteropServices.Marshal.StructureToPtr(object, IntPtr, bool)\"/> (4ms vs 34ms). </para>\n    /// </summary>\n    /// <typeparam name=\"T\"></typeparam>\n    public static class FastStructure<T>\n        where T : struct\n    {\n        /// <summary>\n        /// Delegate that returns a pointer to the provided structure. Use with extreme caution.\n        /// </summary>\n        /// <param name=\"value\"></param>\n        /// <returns></returns>\n        public unsafe delegate void* GetPtrDelegate(ref T value);\n        \n        /// <summary>\n        /// Delegate for loading a structure from the specified memory address\n        /// </summary>\n        /// <param name=\"pointer\"></param>\n        /// <returns></returns>\n        public delegate T PtrToStructureDelegate(IntPtr pointer);\n        \n        /// <summary>\n        /// Delegate for writing a structure to the specified memory address\n        /// </summary>\n        /// <param name=\"value\"></param>\n        /// <param name=\"pointer\"></param>\n        public delegate void StructureToPtrDelegate(ref T value, IntPtr pointer);\n        \n        /// <summary>\n        /// The <see cref=\"GetPtrDelegate\"/> delegate for the generated IL to retrieve a pointer to the structure\n        /// </summary>\n        public unsafe readonly static GetPtrDelegate GetPtr = BuildFunction();\n\n        /// <summary>\n        /// The <see cref=\"PtrToStructureDelegate\"/> delegate for the generated IL to retrieve a structure from a specified memory address.\n        /// </summary>\n        public readonly static PtrToStructureDelegate PtrToStructure = BuildLoadFromPointerFunction();\n\n        /// <summary>\n        /// The <see cref=\"StructureToPtrDelegate\"/> delegate for the generated IL to store a structure at the specified memory address.\n        /// </summary>\n        public readonly static StructureToPtrDelegate StructureToPtr = BuildWriteToPointerFunction();\n\n        /// <summary>\n        /// Cached size of T as determined by <see cref=\"System.Runtime.InteropServices.Marshal.SizeOf(Type)\"/>.\n        /// </summary>\n        public static readonly int Size = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));\n        \n        private static DynamicMethod method;\n        private static DynamicMethod methodLoad;\n        private static DynamicMethod methodWrite;\n\n        /// <summary>\n        /// Performs once of type compatibility check.\n        /// </summary>\n        /// <exception cref=\"ArgumentException\">Thrown if the type T is incompatible</exception>\n        static FastStructure()\n        {\n            // Performs compatibility checks upon T\n            CheckTypeCompatibility(typeof(T));\n        }\n\n        private unsafe static GetPtrDelegate BuildFunction()\n        {\n            method = new DynamicMethod(\"GetStructurePtr<\" + typeof(T).FullName + \">\",\n                typeof(void*), new Type[1] { typeof(T).MakeByRefType() }, typeof(FastStructure).Module);\n\n            ILGenerator generator = method.GetILGenerator();\n            generator.Emit(OpCodes.Ldarg_0);\n            generator.Emit(OpCodes.Conv_U);\n            generator.Emit(OpCodes.Ret);\n            return (GetPtrDelegate)method.CreateDelegate(typeof(GetPtrDelegate));\n        }\n\n        private static unsafe PtrToStructureDelegate BuildLoadFromPointerFunction()\n        {\n            methodLoad = new DynamicMethod(\"PtrToStructure<\" + typeof(T).FullName + \">\",\n                typeof(T), new Type[1] { typeof(IntPtr) }, typeof(FastStructure).Module);\n\n            ILGenerator generator = methodLoad.GetILGenerator();\n            generator.Emit(OpCodes.Ldarg_0);\n            generator.Emit(OpCodes.Ldobj, typeof(T));\n            generator.Emit(OpCodes.Ret);\n\n            return (PtrToStructureDelegate)methodLoad.CreateDelegate(typeof(PtrToStructureDelegate));\n        }\n\n        private static unsafe StructureToPtrDelegate BuildWriteToPointerFunction()\n        {\n            methodWrite = new DynamicMethod(\"StructureToPtr<\" + typeof(T).FullName + \">\",\n                null, new Type[2] { typeof(T).MakeByRefType(), typeof(IntPtr) }, typeof(FastStructure).Module);\n\n            ILGenerator generator = methodWrite.GetILGenerator();\n            generator.Emit(OpCodes.Ldarg_1);\n            generator.Emit(OpCodes.Ldarg_0);\n            generator.Emit(OpCodes.Ldobj, typeof(T));\n            generator.Emit(OpCodes.Stobj, typeof(T));\n            generator.Emit(OpCodes.Ret);\n            return (StructureToPtrDelegate)methodWrite.CreateDelegate(typeof(StructureToPtrDelegate));\n        }\n\n        private static void CheckTypeCompatibility(Type t, System.Collections.Generic.HashSet<Type> checkedItems = null)\n        {\n            if (checkedItems == null)\n            {\n                checkedItems = new System.Collections.Generic.HashSet<Type>();\n                checkedItems.Add(typeof(char));\n                checkedItems.Add(typeof(byte));\n                checkedItems.Add(typeof(sbyte));\n                checkedItems.Add(typeof(bool));\n                checkedItems.Add(typeof(double));\n                checkedItems.Add(typeof(float));\n                checkedItems.Add(typeof(decimal));\n                checkedItems.Add(typeof(int));\n                checkedItems.Add(typeof(short));\n                checkedItems.Add(typeof(long));\n                checkedItems.Add(typeof(uint));\n                checkedItems.Add(typeof(ushort));\n                checkedItems.Add(typeof(ulong));\n                checkedItems.Add(typeof(IntPtr));\n                checkedItems.Add(typeof(void*));\n            }\n\n            if (checkedItems.Contains(t))\n                return;\n            else\n                checkedItems.Add(t);\n\n            FieldInfo[] fi = t.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);\n            foreach (FieldInfo info in fi)\n            {\n                if (!info.FieldType.IsPrimitive && !info.FieldType.IsValueType && !info.FieldType.IsPointer)\n                {\n                    throw new ArgumentException(String.Format(\"Non-value types are not supported: field {0} is of type {1} in structure {2}\", info.Name, info.FieldType.Name, info.DeclaringType.Name));\n                }\n\n                // Example for adding future marshal attributes as incompatible\n                //System.Runtime.InteropServices.MarshalAsAttribute attr;\n                //if (TryGetAttribute<System.Runtime.InteropServices.MarshalAsAttribute>(info, out attr))\n                //{\n                //    if (attr.Value == System.Runtime.InteropServices.UnmanagedType.ByValArray)\n                //    {\n                //        throw new ArgumentException(String.Format(\"UnmanagedType.ByValArray is not supported on field {0} in type [{1}].\", info.Name, typeof(T).FullName));\n                //    }\n                //}\n\n                CheckTypeCompatibility(info.FieldType, checkedItems);\n            }\n        }\n\n        //private static bool TryGetAttribute<T1>(MemberInfo memberInfo, out T1 customAttribute) where T1 : Attribute\n        //{\n        //    var attributes = memberInfo.GetCustomAttributes(typeof(T1), false).FirstOrDefault();\n        //    if (attributes == null)\n        //    {\n        //        customAttribute = null;\n        //        return false;\n        //    }\n        //    customAttribute = (T1)attributes;\n        //    return true;\n        //}\n    }\n}"
  },
  {
    "path": "SharedMemory/MemoryMappedFiles/MemoryMappedFile.cs",
    "content": "﻿// SharedMemory (File: SharedMemory\\MemoryMappedFile.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Security;\nusing System.Text;\nusing Microsoft.Win32.SafeHandles;\nusing System.Security.Permissions;\nusing System.Runtime;\nusing SharedMemory;\nusing System.Runtime.InteropServices;\nusing System.Threading;\n\nnamespace System.IO.MemoryMappedFiles\n{\n#if !NET40Plus\n\n    /// <summary>\n    /// <para>Very limited .NET 3.5 implementation of a managed wrapper around memory-mapped files to reflect the .NET 4 API.</para>\n    /// <para>Only those methods and features necessary for the SharedMemory library have been implemented.</para>\n    /// </summary>\n#if NETFULL\n    [PermissionSet(SecurityAction.LinkDemand)]\n#endif\n    public sealed class MemoryMappedFile: IDisposable\n    {\n        SafeMemoryMappedFileHandle _handle;\n\n        /// <summary>\n        /// Gets the file handle of a memory-mapped file.\n        /// </summary>\n        /// <returns>The handle to the memory-mapped file.</returns>\n        public SafeMemoryMappedFileHandle SafeMemoryMappedFileHandle\n        {\n            [SecurityCritical]\n            [SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)]\n            get\n            {\n                return this._handle;\n            }\n        }\n\n        private MemoryMappedFile(SafeMemoryMappedFileHandle handle)\n        {\n            this._handle = handle;\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        ~MemoryMappedFile()\n        {\n            this.Dispose(false);\n        }\n\n        /// <summary>\n        /// Creates a new memory-mapped file, throwing an IOException if it already exists\n        /// </summary>\n        /// <param name=\"mapName\"></param>\n        /// <param name=\"capacity\"></param>\n        /// <returns></returns>\n        public static MemoryMappedFile CreateNew(String mapName, long capacity)\n        {\n            if (String.IsNullOrEmpty(mapName))\n                throw new ArgumentException(\"mapName cannot be null or empty.\");\n            if (capacity <= 0)\n                throw new ArgumentOutOfRangeException(\"capacity\", \"Value must be larger than 0.\");\n            if (IntPtr.Size == 4 && capacity > ((1024*1024*1024) * (long)4))\n                throw new ArgumentOutOfRangeException(\"capacity\", \"The capacity cannot be greater than the size of the system's logical address space.\");\n            return new MemoryMappedFile(DoCreate(mapName, capacity));\n        }\n\n        [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Interoperability\", \"CA1404:CallGetLastErrorImmediatelyAfterPInvoke\"), SecurityCritical]\n        [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]\n        private static SafeMemoryMappedFileHandle DoCreate(string mapName, long capacity)\n        {\n            SafeFileHandle fileHandle = new SafeFileHandle(new IntPtr(-1), true);\n            SafeMemoryMappedFileHandle safeHandle = null;\n\n            safeHandle = UnsafeNativeMethods.CreateFileMapping(fileHandle, (UnsafeNativeMethods.FileMapProtection)MemoryMappedFileAccess.ReadWrite, capacity, mapName);\n            var lastWin32Error = Marshal.GetLastWin32Error();\n            if (!safeHandle.IsInvalid && (lastWin32Error == UnsafeNativeMethods.ERROR_ALREADY_EXISTS))\n            {\n                throw new System.IO.IOException(UnsafeNativeMethods.GetMessage(lastWin32Error));\n            }\n            else if (safeHandle.IsInvalid && lastWin32Error > 0)\n            {\n                throw new System.IO.IOException(UnsafeNativeMethods.GetMessage(lastWin32Error));\n            }\n\n            if (safeHandle == null || safeHandle.IsInvalid)\n                throw new InvalidOperationException(\"Cannot create file mapping\");\n\n            return safeHandle;\n        }\n\n        /// <summary>\n        /// Creates a new view accessor\n        /// </summary>\n        /// <param name=\"offset\"></param>\n        /// <param name=\"size\"></param>\n        /// <param name=\"access\"></param>\n        /// <returns></returns>\n        [SecurityCritical]\n        [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]\n        public MemoryMappedViewAccessor CreateViewAccessor(long offset, long size, MemoryMappedFileAccess access = MemoryMappedFileAccess.ReadWrite)\n        {\n            if (offset < 0)\n                throw new ArgumentOutOfRangeException(\"offset\", \"Value must be non-negative\");\n            if (size < 0)\n                throw new ArgumentOutOfRangeException(\"size\", \"Value must be positive or zero for default size\");\n            if (IntPtr.Size == 4 && size > ((1024 * 1024 * 1024) * (long)4))\n                throw new ArgumentOutOfRangeException(\"size\", \"The capacity cannot be greater than the size of the system's logical address space.\");\n            MemoryMappedView memoryMappedView = MemoryMappedView.CreateView(this._handle, access, offset, size);\n            return new MemoryMappedViewAccessor(memoryMappedView);\n        }\n\n        /// <summary>\n        /// Dispose pattern\n        /// </summary>\n        public void Dispose()\n        {\n            Dispose(true);\n            GC.SuppressFinalize(this);\n        }\n\n        void Dispose(bool disposeManagedResources)\n        {\n            if (this._handle != null && !this._handle.IsClosed)\n            {\n                this._handle.Dispose();\n                this._handle = null;\n            }\n        }\n\n        /// <summary>\n        /// Opens an existing memory-mapped file. Throws FileNotFoundException if it doesn't exist.\n        /// </summary>\n        /// <param name=\"mapName\"></param>\n        /// <returns></returns>\n        public static MemoryMappedFile OpenExisting(string mapName)\n        {\n            SafeMemoryMappedFileHandle safeMemoryMappedFileHandle = UnsafeNativeMethods.OpenFileMapping((uint)MemoryMappedFileRights.ReadWrite, false, mapName);\n            int lastWin32Error = Marshal.GetLastWin32Error();\n            if (safeMemoryMappedFileHandle.IsInvalid)\n            {\n                if (lastWin32Error == UnsafeNativeMethods.ERROR_FILE_NOT_FOUND)\n                    throw new FileNotFoundException();\n                throw new System.IO.IOException(UnsafeNativeMethods.GetMessage(lastWin32Error));\n            }\n            return new MemoryMappedFile(safeMemoryMappedFileHandle);\n        }\n    }\n\n#endif\n}\n"
  },
  {
    "path": "SharedMemory/MemoryMappedFiles/MemoryMappedFileAccess.cs",
    "content": "﻿// SharedMemory (File: SharedMemory\\MemoryMappedFileAccess.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\nusing SharedMemory;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace System.IO.MemoryMappedFiles\n{\n#if !NET40Plus\n    /// <summary>\n    /// Used when creating a memory mapped file\n    /// </summary>\n    public enum MemoryMappedFileAccess: uint\n    {\n        /// <summary>\n        /// Read\n        /// </summary>\n        Read = 2,\n        /// <summary>\n        /// Read/Write\n        /// </summary>\n        ReadWrite = 4,\n        /// <summary>\n        /// CopyOnWrite\n        /// </summary>\n        CopyOnWrite = 8,\n        /// <summary>\n        /// Read Execute\n        /// </summary>\n        ReadExecute = 32,\n        /// <summary>\n        /// Read/Write Execute\n        /// </summary>\n        ReadWriteExecute = 64\n    }\n\n    internal static class MemoryMappedFileAccessExtensions\n    {\n        internal static UnsafeNativeMethods.FileMapAccess ToMapViewFileAccess(this MemoryMappedFileAccess access)\n        {\n            switch (access)\n            {\n                case MemoryMappedFileAccess.Read:\n                    return UnsafeNativeMethods.FileMapAccess.FileMapRead;\n                case MemoryMappedFileAccess.ReadWrite:\n                    return UnsafeNativeMethods.FileMapAccess.FileMapRead | UnsafeNativeMethods.FileMapAccess.FileMapWrite;\n                case MemoryMappedFileAccess.ReadExecute:\n                    return UnsafeNativeMethods.FileMapAccess.FileMapRead | UnsafeNativeMethods.FileMapAccess.FileMapExecute;\n                case MemoryMappedFileAccess.ReadWriteExecute:\n                    return UnsafeNativeMethods.FileMapAccess.FileMapRead | UnsafeNativeMethods.FileMapAccess.FileMapWrite | UnsafeNativeMethods.FileMapAccess.FileMapExecute;\n                default:\n                    return UnsafeNativeMethods.FileMapAccess.FileMapAllAccess;\n            }\n        }\n    }\n#endif\n}\n"
  },
  {
    "path": "SharedMemory/MemoryMappedFiles/MemoryMappedFileRights.cs",
    "content": "﻿// SharedMemory (File: SharedMemory\\MemoryMappedFileRights.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace System.IO.MemoryMappedFiles\n{\n#if !NET40Plus\n    /// <summary>\n    /// Used for opening a memory-mapped file\n    /// </summary>\n    [Flags]\n    public enum MemoryMappedFileRights: uint\n    {\n        /// <summary>The right to add data to a file or remove data from a file.</summary>\n        Write = 0x02,\n        /// <summary>The right to open and copy a file as read-only.</summary>\n        Read = 0x04,\n        /// <summary>The right to open and copy a file, and the right to add data to a file or remove data from a file.</summary>\n        ReadWrite = MemoryMappedFileRights.Write | MemoryMappedFileRights.Read,\n    }\n#endif\n}\n"
  },
  {
    "path": "SharedMemory/MemoryMappedFiles/MemoryMappedView.cs",
    "content": "﻿// SharedMemory (File: SharedMemory\\MemoryMappedView.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\nusing Microsoft.Win32.SafeHandles;\nusing SharedMemory;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Security.Permissions;\nusing System.Text;\n\nnamespace System.IO.MemoryMappedFiles\n{\n#if !NET40Plus\n    /// <summary>\n    /// <para>Very limited .NET 3.5 implementation of a managed wrapper around memory-mapped files to reflect the .NET 4 API.</para>\n    /// <para>Only those methods and features necessary for the SharedMemory library have been implemented.</para>\n    /// </summary>\n#if NETFULL\n    [PermissionSet(SecurityAction.LinkDemand)]\n#endif\n    public sealed class MemoryMappedView : IDisposable\n    {\n        SafeMemoryMappedViewHandle _handle;\n        \n        /// <summary>\n        /// \n        /// </summary>\n        public SafeMemoryMappedViewHandle SafeMemoryMappedViewHandle\n        {\n            get { return this._handle; }\n        }\n\n        long _size;\n        long _offset;\n\n        /// <summary>\n        /// The size of the view (from offset to end)\n        /// </summary>\n        public long Size { get { return _size; } }\n        \n        /// <summary>\n        /// The start of the view (the handle itself will be aligned based on the allocation granularity)\n        /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa366548(v=vs.85).aspx\n        /// </summary>\n        public long ViewStartOffset { get { return _offset; } }\n\n        private MemoryMappedView(SafeMemoryMappedViewHandle handle, long offset, long size)\n        {\n            this._handle = handle;\n            this._offset = offset;\n            this._size = size;\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        ~MemoryMappedView()\n        {\n            Dispose(false);\n        }\n\n        /// <summary>\n        /// Dispose pattern\n        /// </summary>\n        public void Dispose()\n        {\n            Dispose(true);\n            GC.SuppressFinalize(this);\n        }\n\n        void Dispose(bool disposeManagedResources)\n        {\n            if (this._handle != null && !this._handle.IsClosed)\n                this._handle.Dispose();\n            this._handle = null;\n        }\n\n        [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Interoperability\", \"CA1404:CallGetLastErrorImmediatelyAfterPInvoke\")]\n        internal static MemoryMappedView CreateView(SafeMemoryMappedFileHandle safeMemoryMappedFileHandle, MemoryMappedFileAccess access, long offset, long size)\n        {\n            // http://msdn.microsoft.com/en-us/library/windows/desktop/aa366548(v=vs.85).aspx\n            UnsafeNativeMethods.SYSTEM_INFO info = new UnsafeNativeMethods.SYSTEM_INFO();\n            UnsafeNativeMethods.GetSystemInfo(ref info);\n\n            // To calculate where to start the file mapping, round down the\n            // offset of the data into the memory-mapped file to the nearest multiple of the\n            // system allocation granularity.\n            long fileMapStart = (offset / info.dwAllocationGranularity) * info.dwAllocationGranularity;\n            // How large will the file mapping object be?\n            long mapViewSize = (offset % info.dwAllocationGranularity) + size;\n            // The data of interest is not necessarily at the beginning of the\n            // view, so determine how far into the view to set the pointer.\n            long viewDelta = offset - fileMapStart;\n\n            SafeMemoryMappedViewHandle safeHandle = UnsafeNativeMethods.MapViewOfFile(safeMemoryMappedFileHandle, access.ToMapViewFileAccess(), (ulong)fileMapStart, new UIntPtr((ulong)mapViewSize));\n            var lastWin32Error = Marshal.GetLastWin32Error();\n            if (safeHandle.IsInvalid)\n            {\n                if (lastWin32Error == UnsafeNativeMethods.ERROR_FILE_NOT_FOUND)\n                    throw new FileNotFoundException();\n                throw new System.IO.IOException(UnsafeNativeMethods.GetMessage(lastWin32Error));\n            }\n\n            return new MemoryMappedView(safeHandle, viewDelta, size);\n        }\n    }\n#endif\n}\n"
  },
  {
    "path": "SharedMemory/MemoryMappedFiles/MemoryMappedViewAccessor.cs",
    "content": "﻿// SharedMemory (File: SharedMemory\\MemoryMappedViewAccessor.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\nusing Microsoft.Win32.SafeHandles;\nusing SharedMemory;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Security;\nusing System.Security.Permissions;\nusing System.Text;\n\nnamespace System.IO.MemoryMappedFiles\n{\n#if !NET40Plus\n    /// <summary>\n    /// \n    /// </summary>\n#if NETFULL\n    [PermissionSet(SecurityAction.LinkDemand)]\n#endif\n    public sealed class MemoryMappedViewAccessor : IDisposable\n    {\n        MemoryMappedView _view;\n\n        internal MemoryMappedViewAccessor(MemoryMappedView memoryMappedView)\n        {\n            this._view = memoryMappedView;\n        }\n        \n        /// <summary>\n        /// \n        /// </summary>\n        public SafeMemoryMappedViewHandle SafeMemoryMappedViewHandle\n        {\n            [SecurityCritical]\n            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]\n            get\n            {\n                return this._view.SafeMemoryMappedViewHandle;\n            }\n        }\n\n        /// <summary>\n        /// Dispose pattern\n        /// </summary>\n        public void Dispose()\n        {\n            Dispose(true);\n            GC.SuppressFinalize(this);\n        }\n\n        private void Dispose(bool disposeManagedResources)\n        {\n            if (_view != null)\n                _view.Dispose();\n            _view = null;\n        }\n\n        internal static unsafe void PtrToStructure<T>(byte* ptr, out T structure)\n            where T : struct\n        {\n            structure = FastStructure.PtrToStructure<T>((IntPtr)ptr);\n            //var tr = __makeref(structure);\n            //*(IntPtr*)&tr = (IntPtr)ptr;\n            //structure = __refvalue( tr,T);\n        }\n\n        internal static unsafe void StructureToPtr<T>(ref T structure, byte* ptr)\n            where T : struct\n        {\n            FastStructure.StructureToPtr<T>(ref structure, (IntPtr)ptr);\n        }\n\n        internal unsafe void Write<T>(long position, ref T structure)\n            where T: struct\n        {\n            uint elementSize = (uint)Marshal.SizeOf(typeof(T));\n            if (position > this._view.Size - elementSize)\n                throw new ArgumentOutOfRangeException(\"position\", \"\");\n\n            try\n            {\n                byte* ptr = null;\n                _view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);\n                ptr += +_view.ViewStartOffset + position;\n                StructureToPtr(ref structure, ptr);\n            }\n            finally\n            {\n                _view.SafeMemoryMappedViewHandle.ReleasePointer();\n            }\n        }\n\n        internal unsafe void WriteArray<T>(long position, T[] buffer, int index, int count)\n            where T : struct\n        {\n            uint elementSize = (uint)Marshal.SizeOf(typeof(T));\n\n            if (position > this._view.Size - (elementSize * count))\n                throw new ArgumentOutOfRangeException(\"position\");\n            \n            try\n            {\n                byte* ptr = null;\n                _view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);\n                ptr += _view.ViewStartOffset + position;\n\n                FastStructure.WriteArray<T>((IntPtr)ptr, buffer, index, count);\n\n                //for (var i = 0; i < count; i++)\n                //{\n                //    StructureToPtr(ref buffer[index + i], ptr + (i * elementSize));\n                //}\n            }\n            finally\n            {\n                _view.SafeMemoryMappedViewHandle.ReleasePointer();\n            }\n        }\n\n        internal unsafe void Read<T>(long position, out T structure)\n            where T: struct\n        {\n            uint size = (uint)Marshal.SizeOf(typeof(T));\n            if (position > this._view.Size - size)\n                throw new ArgumentOutOfRangeException(\"position\", \"\");\n            try\n            {\n                byte* ptr = null;\n                _view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);\n                ptr += _view.ViewStartOffset + position;\n                PtrToStructure(ptr, out structure);\n            }\n            finally\n            {\n                _view.SafeMemoryMappedViewHandle.ReleasePointer();\n            }\n        }\n\n        internal unsafe void ReadArray<T>(long position, T[] buffer, int index, int count)\n            where T : struct\n        {\n            uint elementSize = (uint)FastStructure.SizeOf<T>();\n\n            if (buffer == null)\n                throw new ArgumentNullException(\"buffer\");\n            if (position > this._view.Size - (elementSize * count))\n                throw new ArgumentOutOfRangeException(\"position\");\n            try\n            {\n                byte* ptr = null;\n                _view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);\n                ptr += _view.ViewStartOffset + position;\n\n                FastStructure.ReadArray<T>(buffer, (IntPtr)ptr, index, count);\n                \n                //for (var i = 0; i < count; i++)\n                //{\n                //    PtrToStructure(ptr + (i * elementSize), out buffer[index + i]);\n                //}\n            }\n            finally\n            {\n                _view.SafeMemoryMappedViewHandle.ReleasePointer();\n            }\n        }\n    }\n#endif\n}\n"
  },
  {
    "path": "SharedMemory/MemoryMappedFiles/SafeMemoryMappedFileHandle.cs",
    "content": "﻿// SharedMemory (File: SharedMemory\\safememorymappedfilehandle.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\nusing SharedMemory;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Security.Permissions;\nusing System.Text;\n\nnamespace Microsoft.Win32.SafeHandles\n{\n#if !NET40Plus\n    /// <summary>\n    /// Provides a safe handle that represents a memory-mapped file for sequential access.\n    /// </summary>\n    public sealed class SafeMemoryMappedFileHandle: SafeHandleZeroOrMinusOneIsInvalid\n    {\n        [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]\n        internal SafeMemoryMappedFileHandle()\n            : base(true)\n        {\n        }\n\n        [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]\n        internal SafeMemoryMappedFileHandle(IntPtr handle, bool ownsHandle)\n            : base(ownsHandle)\n        {\n            base.SetHandle(handle);\n        }\n\n        /// <summary>\n        /// Closes the memory-mapped file handle\n        /// </summary>\n        /// <returns></returns>\n        protected override bool ReleaseHandle()\n        {\n            try\n            {\n                return UnsafeNativeMethods.CloseHandle(this.handle);\n            }\n            finally\n            {\n                this.handle = IntPtr.Zero;\n            }\n        }\n    }\n#endif\n}\n"
  },
  {
    "path": "SharedMemory/MemoryMappedFiles/SafeMemoryMappedViewHandle.cs",
    "content": "﻿// SharedMemory (File: SharedMemory\\safememorymappedviewhandle.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\nusing SharedMemory;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Security.Permissions;\nusing System.Text;\n\nnamespace Microsoft.Win32.SafeHandles\n{\n#if !NET40Plus\n    /// <summary>\n    /// Provides a safe handle that represents a view of a block of unmanaged memory for random access.\n    /// </summary>\n#if NETFULL\n    [PermissionSet(SecurityAction.LinkDemand)]\n#endif\n    public sealed class SafeMemoryMappedViewHandle: SafeHandleZeroOrMinusOneIsInvalid\n    {\n        [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]\n        internal SafeMemoryMappedViewHandle()\n            : base(true)\n        {\n        }\n\n        [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]\n        internal SafeMemoryMappedViewHandle(IntPtr handle, bool ownsHandle)\n            : base(ownsHandle)\n        {\n            base.SetHandle(handle);\n        }\n\n        /// <summary>\n        /// Unmap's the view of the file\n        /// </summary>\n        /// <returns></returns>\n        protected override bool ReleaseHandle()\n        {\n            try\n            {\n                return UnsafeNativeMethods.UnmapViewOfFile(this.handle);\n            }\n            finally\n            {\n                this.handle = IntPtr.Zero;\n            }\n            \n        }\n\n        /// <summary>\n        /// Acquires a reference to the pointer, incrementing the internal ref count. Should be followed by corresponding call to <see cref=\"ReleasePointer\"/>\n        /// </summary>\n        /// <param name=\"pointer\"></param>\n        public unsafe void AcquirePointer(ref byte* pointer)\n        {\n            bool flag = false;\n            base.DangerousAddRef(ref flag);\n            pointer = (byte*)this.handle.ToPointer();\n        }\n\n        /// <summary>\n        /// Release the pointer\n        /// </summary>\n        public void ReleasePointer()\n        {\n            base.DangerousRelease();\n        }\n    }\n#endif\n}\n"
  },
  {
    "path": "SharedMemory/RpcBuffer.cs",
    "content": "﻿// SharedMemory (File: SharedMemory\\RpcBuffer.cs)\n// Copyright (c) 2020 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Text;\n\nnamespace SharedMemory\n{\n    // Only supported in .NET 4.5+ and .NET Standard 2.0\n\n    using System.Collections.Concurrent;\n    using System.Threading;\n    using System.Threading.Tasks;\n\n    internal enum InstanceType\n    {\n        Master,\n        Slave\n    }\n\n    /// <summary>\n    /// The available RPC protocols\n    /// </summary>\n    public enum RpcProtocol\n    {\n        /// <summary>\n        /// Version 1 - messages are split into packets that fit the buffer capacity and include a protocol header in each packet\n        /// </summary>\n        V1 = 1\n    }\n\n    /// <summary>\n    /// Extension methods for adorning RPC tasks\n    /// </summary>\n\n    public static class ResponseTaskHelper\n    {\n        static readonly RpcResponse CancelledRpcResponse = new RpcResponse(false, null);\n        static readonly Task<RpcResponse> CancelledRpcResponseTask = Task.FromResult(CancelledRpcResponse);\n\n        /// <summary>\n        /// Adds timeout and manual cancellation capabilities to an existing Task\n        /// </summary>\n        /// <param name=\"task\"></param>\n        /// <param name=\"millisecondsTimeout\"></param>\n        /// <param name=\"cancellationToken\"></param>\n        /// <returns></returns>\n        public static async Task<RpcResponse> TimeoutOrCancel(this Task<RpcResponse> task, int millisecondsTimeout, CancellationToken cancellationToken = default)\n        {\n            if (task.IsCompleted)\n            {\n                // the task has already completed\n                // No proxy necessary.\n                return await task.ConfigureAwait(false);\n            }\n\n            // Short-circuit #2: zero timeout\n            if (millisecondsTimeout == 0)\n            {\n                // We've already timed out.\n                return new RpcResponse(false, null);\n            }\n\n            if (millisecondsTimeout == Timeout.Infinite)\n            {\n                return await task.WaitAsync(cancellationToken).ConfigureAwait(false);\n            }\n\n            using (var cts = new CancellationTokenSource(millisecondsTimeout))\n            {\n                var timeoutToken = cts.Token;\n                return await task.WaitAsync(cancellationToken).WaitAsync(timeoutToken).ConfigureAwait(false);\n            }\n        }\n\n        /// <summary>\n        /// Asynchronously waits for the task to complete, or for the cancellation token to be canceled.\n        /// </summary>\n        /// <param name=\"this\">The task to wait for. May not be <c>null</c>.</param>\n        /// <param name=\"cancellationToken\">The cancellation token that cancels the wait.</param>\n        private static Task<RpcResponse> WaitAsync(this Task<RpcResponse> @this, CancellationToken cancellationToken)\n        {\n            if (@this == null)\n                throw new ArgumentNullException(nameof(@this));\n\n            if (!cancellationToken.CanBeCanceled)\n                return @this;\n            if (cancellationToken.IsCancellationRequested)\n                return CancelledRpcResponseTask;\n            return DoWaitAsync(@this, cancellationToken);\n        }\n\n        private static async Task<RpcResponse> DoWaitAsync(Task<RpcResponse> task, CancellationToken cancellationToken)\n        {\n            using (var cancelTaskSource = new RpcResponseCancellationTokenTaskSource(cancellationToken))\n                return await (await Task.WhenAny(task, cancelTaskSource.Task).ConfigureAwait(false)).ConfigureAwait(false);\n        }\n        \n        /// <summary>\n        /// Holds the task for a cancellation token, as well as the token registration. The registration is disposed when this instance is disposed.\n        /// </summary>\n        public sealed class RpcResponseCancellationTokenTaskSource : IDisposable\n        {\n            /// <summary>\n            /// The cancellation token registration, if any. This is <c>null</c> if the registration was not necessary.\n            /// </summary>\n            private readonly IDisposable _registration;\n\n            /// <summary>\n            /// Creates a task for the specified cancellation token, registering with the token if necessary.\n            /// </summary>\n            /// <param name=\"cancellationToken\">The cancellation token to observe.</param>\n            public RpcResponseCancellationTokenTaskSource(CancellationToken cancellationToken)\n            {\n                if (cancellationToken.IsCancellationRequested)\n                {\n                    Task = CancelledRpcResponseTask;\n                    return;\n                }\n                var tcs = new TaskCompletionSource<RpcResponse>();\n                _registration = cancellationToken.Register(() => tcs.TrySetResult(CancelledRpcResponse), useSynchronizationContext: false);\n                Task = tcs.Task;\n            }\n\n            /// <summary>\n            /// Gets the task for the source cancellation token.\n            /// </summary>\n            public Task<RpcResponse> Task { get; private set; }\n\n            /// <summary>\n            /// Disposes the cancellation token registration, if any. Note that this may cause <see cref=\"Task\"/> to never complete.\n            /// </summary>\n            public void Dispose()\n            {\n                _registration?.Dispose();\n            }\n        }\n    }\n\n    /// <summary>\n    /// The RPC message type\n    /// </summary>\n    public enum MessageType : byte\n    {\n        /// <summary>\n        /// A request message\n        /// </summary>\n        RpcRequest = 1,\n        /// <summary>\n        /// A response message\n        /// </summary>\n        RpcResponse = 2,\n        /// <summary>\n        /// An error message\n        /// </summary>\n        ErrorInRpc = 3,\n    }\n\n    /// <summary>\n    /// The V1 protocol header\n    /// </summary>\n    public struct RpcProtocolHeaderV1\n    {\n        /// <summary>\n        /// Message Type\n        /// </summary>\n        public MessageType MsgType;\n        /// <summary>\n        /// Message Id\n        /// </summary>\n        public ulong MsgId;\n        /// <summary>\n        /// Total message size\n        /// </summary>\n        public int PayloadSize;\n        /// <summary>\n        /// The current packet number\n        /// </summary>\n        public ushort CurrentPacket;\n        /// <summary>\n        /// The total number of packets in the message\n        /// </summary>\n        public ushort TotalPackets;\n        /// <summary>\n        /// If a response, the Id of the remote message this is a response to\n        /// </summary>\n        public ulong ResponseId;\n    }\n\n    /// <summary>\n    /// Represents a request to be sent on the channel\n    /// </summary>\n    public class RpcRequest\n    {\n        internal RpcRequest() { }\n        /// <summary>\n        /// The message Id\n        /// </summary>\n        public ulong MsgId { get; set; }\n        /// <summary>\n        /// The message type\n        /// </summary>\n        public MessageType MsgType { get; set; }\n        /// <summary>\n        /// The message payload (if any)\n        /// </summary>\n        public byte[] Data { get; set; }\n        /// <summary>\n        /// A wait event that is signaled when a response is ready\n        /// </summary>\n        public TaskCompletionSource<RpcResponse> ResponseReady { get; } = new TaskCompletionSource<RpcResponse>();\n        /// <summary>\n        /// Was the request successful\n        /// </summary>\n        public bool IsSuccess { get; internal set; }\n        /// <summary>\n        /// When the request was created\n        /// </summary>\n        public DateTime Created { get; } = DateTime.Now;\n    }\n\n    /// <summary>\n    /// Represents the result of a remote request.\n    /// </summary>\n    public class RpcResponse\n    {\n        /// <summary>\n        /// Constructs an RpcResponse\n        /// </summary>\n        /// <param name=\"success\">was it a success</param>\n        /// <param name=\"data\">the message data (if any)</param>\n        public RpcResponse(bool success, byte[] data)\n        {\n            this.Success = success;\n            this.Data = data;\n        }\n\n        /// <summary>\n        /// If the request was successful\n        /// </summary>\n        public bool Success { get; }\n\n        /// <summary>\n        /// The returned result (if applicable)\n        /// </summary>\n        public byte[] Data { get; }\n    }\n\n    /// <summary>\n    /// Represents the channel statistics of an <see cref=\"RpcBuffer\"/> instance\n    /// </summary>\n    public class RpcStatistics\n    {\n        /// <summary>\n        /// The protocol overhead per packet\n        /// </summary>\n        public int ProtocolOverheadPerPacket { get; internal set; }\n\n        /// <summary>\n        /// Bytes read from channel (excluding protocol overhead)\n        /// </summary>\n        public ulong BytesRead { get; private set; }\n        /// <summary>\n        /// Number of packets read from channel\n        /// </summary>\n        public ulong PacketsRead { get; private set; }\n        /// <summary>\n        /// The largest packet read from channel (excluding protocol overhead)\n        /// </summary>\n        public int ReadingMaxPacketSize { get; private set; }\n        /// <summary>\n        /// The size of last packet read from channel (excluding protocol overhead)\n        /// </summary>\n        public int ReadingLastPacketSize { get; private set; } = -1;\n        /// <summary>\n        /// The size of last message read from channel (excluding protocol overhead)\n        /// </summary>\n        public int ReadingLastMessageSize { get; private set; } = -1;\n\n        /// <summary>\n        /// The total number of messages received\n        /// </summary>\n        public ulong MessagesReceived { get { return RequestsReceived + ResponsesReceived + ErrorsReceived; } }\n\n        /// <summary>\n        /// The number of request messages received\n        /// </summary>\n        public ulong RequestsReceived { get; private set; }\n\n        /// <summary>\n        /// The number of response messages received\n        /// </summary>\n        public ulong ResponsesReceived { get; private set; }\n\n        /// <summary>\n        /// The number of error message received\n        /// </summary>\n        public ulong ErrorsReceived { get; private set; }\n\n        /// <summary>\n        /// The number of bytes written to channel (excluding protocol overhead)\n        /// </summary>\n        public ulong BytesWritten { get; private set; }\n        /// <summary>\n        /// The number of packets written to channel\n        /// </summary>\n        public ulong PacketsWritten { get; private set; }\n        /// <summary>\n        /// The largest packet written to channel (excluding protocol overhead)\n        /// </summary>\n        public int WritingMaxPacketSize { get; private set; }\n\n        /// <summary>\n        /// The size of last packet written to channel (excluding protocol overhead)\n        /// </summary>\n        public int WritingLastPacketSize { get; private set; } = -1;\n\n        /// <summary>\n        /// The size of last message written to channel (excluding protocol overhead)\n        /// </summary>\n        public int WritingLastMessageSize { get; private set; } = -1;\n\n        /// <summary>\n        /// Number of response messages received that were discarded (provided a non-existent message Id)\n        /// </summary>\n        public ulong DiscardedResponses { get; private set; }\n        /// <summary>\n        /// The response message Id that was last discarded\n        /// </summary>\n        public ulong LastDiscardedResponseId { get; private set; }\n\n        /// <summary>\n        /// The total number of messages sent\n        /// </summary>\n        public ulong MessagesSent { get { return RequestsSent + ResponsesSent + ErrorsSent; } }\n\n        /// <summary>\n        /// The number of request messages sent\n        /// </summary>\n        public ulong RequestsSent { get; private set; }\n\n        /// <summary>\n        /// The number of response messages sent\n        /// </summary>\n        public ulong ResponsesSent { get; private set; }\n\n        /// <summary>\n        /// The number of error messages sent\n        /// </summary>\n        public ulong ErrorsSent { get; private set; }\n\n        /// <summary>\n        /// Number of timeouts\n        /// </summary>\n        public ulong Timeouts { get; private set; }\n        /// <summary>\n        /// DateTime of last timeout\n        /// </summary>\n        public DateTime LastTimeout { get; private set; }\n        DateTime StartWaitWriteTimestamp { get; set; }\n        DateTime EndWaitWriteTimestamp { get; set; }\n        /// <summary>\n        /// Maximum Ticks waited for available write slot\n        /// </summary>\n        public long MaxWaitWriteTicks { get; private set; } = -1;\n        DateTime StartWaitReadTimestamp { get; set; }\n        DateTime EndWaitReadTimestamp { get; set; }\n        /// <summary>\n        /// Maximum Ticks waiting for read slot (cannot exceed 1sec)\n        /// </summary>\n        public long MaxWaitReadTicks { get; private set; } = -1;\n\n        internal void StartWaitRead()\n        {\n            StartWaitReadTimestamp = DateTime.Now;\n        }\n\n        internal void ReadPacket(int bytes)\n        {\n            EndWaitReadTimestamp = DateTime.Now;\n\n            var ticks = EndWaitReadTimestamp.Ticks - StartWaitReadTimestamp.Ticks;\n            if (ticks > MaxWaitReadTicks)\n            {\n                MaxWaitReadTicks = ticks;\n            }\n\n            PacketsRead++;\n            BytesRead += (ulong)bytes;\n            ReadingLastPacketSize = bytes;\n            if (bytes > ReadingMaxPacketSize)\n            {\n                ReadingMaxPacketSize = bytes;\n            }\n        }\n\n        internal void StartWaitWrite()\n        {\n            StartWaitWriteTimestamp = DateTime.Now;\n        }\n\n        internal void WritePacket(int bytes)\n        {\n            EndWaitWriteTimestamp = DateTime.Now;\n\n            var ticks = EndWaitWriteTimestamp.Ticks - StartWaitWriteTimestamp.Ticks;\n            if (ticks > MaxWaitWriteTicks)\n            {\n                MaxWaitWriteTicks = ticks;\n            }\n\n            PacketsWritten++;\n            BytesWritten += (ulong)bytes;\n            WritingLastPacketSize = bytes;\n            if (bytes > WritingMaxPacketSize)\n            {\n                WritingMaxPacketSize = bytes;\n            }\n        }\n\n        internal void MessageReceived(MessageType msgType, int size)\n        {\n            ReadingLastMessageSize = size;\n            switch (msgType)\n            {\n                case MessageType.RpcRequest:\n                    RequestsReceived++;\n                    break;\n                case MessageType.RpcResponse:\n                    ResponsesReceived++;\n                    break;\n                case MessageType.ErrorInRpc:\n                    ErrorsReceived++;\n                    break;\n            }\n        }\n\n        internal void MessageSent(MessageType msgType, int size)\n        {\n            WritingLastMessageSize = size;\n            switch (msgType)\n            {\n                case MessageType.RpcRequest:\n                    RequestsSent++;\n                    break;\n                case MessageType.RpcResponse:\n                    ResponsesSent++;\n                    break;\n                case MessageType.ErrorInRpc:\n                    ErrorsSent++;\n                    break;\n            }\n        }\n\n        internal void Timeout()\n        {\n            Timeouts++;\n            LastTimeout = DateTime.Now;\n        }\n\n        internal void DiscardResponse(ulong msgId)\n        {\n            DiscardedResponses++;\n            LastDiscardedResponseId = msgId;\n        }\n\n        /// <summary>\n        /// Reset all statistics\n        /// </summary>\n        public void Reset()\n        {\n            Timeouts = 0;\n            LastTimeout = DateTime.MinValue;\n            PacketsRead = 0;\n            BytesRead = 0;\n            MaxWaitReadTicks = -1;\n            ReadingMaxPacketSize = 0;\n            ReadingLastMessageSize = -1;\n            ReadingLastPacketSize = -1;\n            RequestsReceived = 0;\n            ResponsesReceived = 0;\n            ErrorsReceived = 0;\n            PacketsWritten = 0;\n            BytesWritten = 0;\n            MaxWaitWriteTicks = -1;\n            WritingMaxPacketSize = 0;\n            WritingLastMessageSize = -1;\n            WritingLastPacketSize = -1;\n            RequestsSent = 0;\n            ResponsesSent = 0;\n            ErrorsSent = 0;\n            DiscardedResponses = 0;\n            LastDiscardedResponseId = 0;\n        }\n    }\n\n    /// <summary>\n    /// A simple RPC implementation designed for a single master/slave pair\n    /// </summary>\n    public class RpcBuffer : IDisposable\n    {\n        private Mutex masterMutex;\n        private long _disposed = 0;\n\n        /// <summary>\n        /// Whether the RpcBuffer has been disposed\n        /// </summary>\n        protected bool Disposed\n        {\n            get\n            {\n                return Interlocked.Read(ref _disposed) != 0;\n            }\n        }\n\n        /// <summary>\n        /// Dispose has completed\n        /// </summary>\n        public bool DisposeFinished\n        {\n            get\n            {\n                return Interlocked.Read(ref _disposed) == 2;\n            }\n        }\n\n        private readonly InstanceType instanceType;\n        private readonly RpcProtocol protocolVersion;\n        private readonly int protocolLength;\n        private readonly int bufferCapacity;\n        private readonly int bufferNodeCount;\n        private readonly int msgBufferLength; // The amount of room left in the node after protocol header\n\n        /// <summary>\n        /// The buffer used to send messages to remote channel endpoint\n        /// </summary>\n        protected CircularBuffer WriteBuffer { get; private set; }\n        /// <summary>\n        /// The buffer used to receive message from the remote channel endpoint\n        /// </summary>\n        protected CircularBuffer ReadBuffer { get; private set; }\n\n        /// <summary>\n        /// Channel endpoint statistics\n        /// </summary>\n        public RpcStatistics Statistics { get; private set; }\n\n        const int defaultTimeoutMs = 30000;\n        object lock_sendQ = new object();\n\n        /// <summary>\n        /// Outgoing requests waiting for responses\n        /// </summary>\n        protected ConcurrentDictionary<ulong, RpcRequest> Requests { get; } = new ConcurrentDictionary<ulong, RpcRequest>();\n        /// <summary>\n        /// Incoming requests waiting for more packets\n        /// </summary>\n        protected ConcurrentDictionary<ulong, RpcRequest> IncomingRequests { get; } = new ConcurrentDictionary<ulong, RpcRequest>();\n\n        Action<ulong, byte[]> RemoteCallHandler = null;\n        Func<ulong, byte[], Task> AsyncRemoteCallHandler = null;\n        Func<ulong, byte[], byte[]> RemoteCallHandlerWithResult = null;\n        Func<ulong, byte[], Task<byte[]>> AsyncRemoteCallHandlerWithResult = null;\n\n        /// <summary>\n        /// Construct a new RpcBuffer\n        /// </summary>\n        /// <param name=\"name\">The channel name. This is the name to be shared between the master/slave pair. Each pair must have a unique value.</param>\n        /// <param name=\"remoteCallHandler\">Action to handle requests with no response.</param>\n        /// <param name=\"bufferCapacity\">Master only: Maximum buffer capacity. Messages will be split into packets that fit this capacity (including a packet header of 64-bytes). The slave will use the same size as defined by the master</param>\n        /// <param name=\"protocolVersion\">ProtocolVersion.V1 = 64-byte header for each packet</param>\n        /// <param name=\"bufferNodeCount\">Master only: The number of nodes in the underlying circular buffers, each with a size of <paramref name=\"bufferCapacity\"/></param>\n        public RpcBuffer(string name, Action<ulong, byte[]> remoteCallHandler, int bufferCapacity = 50000, RpcProtocol protocolVersion = RpcProtocol.V1, int bufferNodeCount = 10) :\n            this(name, bufferCapacity, protocolVersion, bufferNodeCount)\n        {\n            RemoteCallHandler = remoteCallHandler;\n        }\n\n        /// <summary>\n        /// Construct a new RpcBuffer\n        /// </summary>\n        /// <param name=\"name\">The unique channel name. This is the name to be shared between the master/slave pair. Each pair must have a unique value.</param>\n        /// <param name=\"asyncRemoteCallHandler\">Asynchronous action to handle requests with no response.</param>\n        /// <param name=\"bufferCapacity\">Master only: Maximum buffer capacity. Messages will be split into packets that fit this capacity (including a packet header of 64-bytes). The slave will use the same size as defined by the master</param>\n        /// <param name=\"protocolVersion\">ProtocolVersion.V1 = 64-byte header for each packet</param>\n        /// <param name=\"bufferNodeCount\">Master only: The number of nodes in the underlying circular buffers, each with a size of <paramref name=\"bufferCapacity\"/></param>\n        public RpcBuffer(string name, Func<ulong, byte[], Task> asyncRemoteCallHandler, int bufferCapacity = 50000, RpcProtocol protocolVersion = RpcProtocol.V1, int bufferNodeCount = 10) :\n            this(name, bufferCapacity, protocolVersion, bufferNodeCount)\n        {\n            AsyncRemoteCallHandler = asyncRemoteCallHandler;\n        }\n\n        /// <summary>\n        /// Construct a new RpcBuffer\n        /// </summary>\n        /// <param name=\"name\">The unique channel name. This is the name to be shared between the master/slave pair. Each pair must have a unique value.</param>\n        /// <param name=\"remoteCallHandlerWithResult\">Function to handle requests with a response.</param>\n        /// <param name=\"bufferCapacity\">Master only: Maximum buffer capacity. Messages will be split into packets that fit this capacity (including a packet header of 64-bytes). The slave will use the same size as defined by the master</param>\n        /// <param name=\"protocolVersion\">ProtocolVersion.V1 = 64-byte header for each packet</param>\n        /// <param name=\"bufferNodeCount\">Master only: The number of nodes in the underlying circular buffers, each with a size of <paramref name=\"bufferCapacity\"/></param>\n        public RpcBuffer(string name, Func<ulong, byte[], byte[]> remoteCallHandlerWithResult, int bufferCapacity = 50000, RpcProtocol protocolVersion = RpcProtocol.V1, int bufferNodeCount = 10) :\n            this(name, bufferCapacity, protocolVersion, bufferNodeCount)\n        {\n            RemoteCallHandlerWithResult = remoteCallHandlerWithResult;\n        }\n\n        /// <summary>\n        /// Construct a new RpcBuffer\n        /// </summary>\n        /// <param name=\"name\">The unique channel name. This is the name to be shared between the master/slave pair. Each pair must have a unique value.</param>\n        /// <param name=\"asyncRemoteCallHandlerWithResult\">Function to asynchronously handle requests with a response.</param>\n        /// <param name=\"bufferCapacity\">Master only: Maximum buffer capacity. Messages will be split into packets that fit this capacity (including a packet header of 64-bytes). The slave will use the same size as defined by the master</param>\n        /// <param name=\"protocolVersion\">ProtocolVersion.V1 = 64-byte header for each packet</param>\n        /// <param name=\"bufferNodeCount\">Master only: The number of nodes in the underlying circular buffers, each with a size of <paramref name=\"bufferCapacity\"/></param>\n        public RpcBuffer(string name, Func<ulong, byte[], Task<byte[]>> asyncRemoteCallHandlerWithResult, int bufferCapacity = 50000, RpcProtocol protocolVersion = RpcProtocol.V1, int bufferNodeCount = 10) :\n            this(name, bufferCapacity, protocolVersion, bufferNodeCount)\n        {\n            AsyncRemoteCallHandlerWithResult = asyncRemoteCallHandlerWithResult;\n        }\n\n        /// <summary>\n        /// Construct a new RpcBuffer\n        /// </summary>\n        /// <param name=\"name\">The unique channel name. This is the name to be shared between the master/slave pair. Each pair must have a unique value.</param>\n        /// <param name=\"bufferCapacity\">Master only: Maximum buffer capacity. Messages will be split into packets that fit this capacity (including a packet header of 64-bytes). The slave will use the same size as defined by the master</param>\n        /// <param name=\"protocolVersion\">ProtocolVersion.V1 = 64-byte header for each packet</param>\n        /// <param name=\"bufferNodeCount\">Master only: The number of nodes in the underlying circular buffers, each with a size of <paramref name=\"bufferCapacity\"/></param>\n        public RpcBuffer(string name, int bufferCapacity = 50000, RpcProtocol protocolVersion = RpcProtocol.V1, int bufferNodeCount = 10)\n        {\n            if (bufferCapacity < 256) // min 256 bytes\n            {\n                throw new ArgumentOutOfRangeException(nameof(bufferCapacity), \"cannot be less than 256 bytes\");\n            }\n\n            if (bufferCapacity > 1024 * 1024) // max 1MB\n            {\n                throw new ArgumentOutOfRangeException(nameof(bufferCapacity), \"cannot be larger than 1MB\");\n            }\n\n            Statistics = new RpcStatistics();\n\n            masterMutex = new Mutex(true, name + \"SharedMemory_MasterMutex\", out bool createdNew);\n\n            if (createdNew && masterMutex.WaitOne(500))\n            {\n                instanceType = InstanceType.Master;\n            }\n            else\n            {\n                instanceType = InstanceType.Slave;\n                if (masterMutex != null)\n                {\n                    masterMutex.Close();\n                    masterMutex.Dispose();\n                    masterMutex = null;\n                }\n            }\n\n            switch (protocolVersion)\n            {\n                case RpcProtocol.V1:\n                    this.protocolVersion = protocolVersion;\n                    protocolLength = FastStructure.SizeOf<RpcProtocolHeaderV1>();\n                    Statistics.ProtocolOverheadPerPacket = protocolLength;\n                    break;\n            }\n\n            this.bufferCapacity = bufferCapacity;\n            this.bufferNodeCount = bufferNodeCount;\n            if (instanceType == InstanceType.Master)\n            {\n                WriteBuffer = new CircularBuffer(name + \"_Slave_SharedMemory_MMF\", bufferNodeCount, this.bufferCapacity);\n                ReadBuffer = new CircularBuffer(name + \"_Master_SharedMemory_MMF\", bufferNodeCount, this.bufferCapacity);\n            }\n            else\n            {\n                ReadBuffer = new CircularBuffer(name + \"_Slave_SharedMemory_MMF\");\n                WriteBuffer = new CircularBuffer(name + \"_Master_SharedMemory_MMF\");\n                this.bufferCapacity = ReadBuffer.NodeBufferSize;\n                this.bufferNodeCount = ReadBuffer.NodeCount;\n            }\n\n            this.msgBufferLength = Convert.ToInt32(this.bufferCapacity) - protocolLength;\n\n\n            Task readTask = new Task(() =>\n            {\n                switch (protocolVersion)\n                {\n                    case RpcProtocol.V1:\n                        ReadThreadV1();\n                        break;\n                }\n            }, TaskCreationOptions.LongRunning);\n\n            readTask.Start();\n        }\n\n        object mutex = new object();\n        ulong messageId = 1;\n\n        /// <summary>\n        /// Constructs a new request message, giving it a new unique MsgId\n        /// </summary>\n        /// <returns></returns>\n        protected RpcRequest CreateMessageRequest()\n        {\n            RpcRequest request = new RpcRequest();\n            lock (mutex)\n            {\n                request.MsgId = messageId++;\n            }\n\n            return request;\n        }\n\n        /// <summary>\n        /// Send a remote request on the channel, blocking until a result is returned\n        /// </summary>\n        /// <param name=\"args\">Arguments (if any) as a byte array to be sent to the remote endpoint</param>\n        /// <param name=\"timeoutMs\">Timeout in milliseconds (defaults to 30sec)</param>\n        /// <param name=\"cancellationToken\">A cancellation token</param>\n        /// <returns>The returned response</returns>\n        /// <exception cref=\"ObjectDisposedException\">Thrown if this object has been disposed</exception>\n        /// <exception cref=\"InvalidOperationException\">Thrown if the underlying buffers have been closed by the channel owner</exception>\n        public RpcResponse RemoteRequest(byte[] args = null, int timeoutMs = defaultTimeoutMs, CancellationToken cancellationToken = default)\n        {\n            ThrowIfDisposedOrShutdown();\n\n            var request = CreateMessageRequest();\n            Task<RpcResponse> sendMessage = SendMessage(request, args, timeoutMs, cancellationToken);\n            return sendMessage.Result;\n        }\n\n        /// <summary>\n        /// Send a remote request on the channel (awaitable)\n        /// </summary>\n        /// <param name=\"args\">Arguments (if any) as a byte array to be sent to the remote endpoint</param>\n        /// <param name=\"timeoutMs\">Timeout in milliseconds (defaults to 30sec)</param>\n        /// <param name=\"cancellationToken\">A cancellation token</param>\n        /// <returns></returns>\n        /// <exception cref=\"ObjectDisposedException\">Thrown if this object has been disposed</exception>\n        /// <exception cref=\"InvalidOperationException\">Thrown if the underlying buffers have been closed by the channel owner</exception>\n        public Task<RpcResponse> RemoteRequestAsync(byte[] args = null, int timeoutMs = defaultTimeoutMs, CancellationToken cancellationToken = default)\n        {\n            ThrowIfDisposedOrShutdown();\n\n            var request = CreateMessageRequest();\n            return SendMessage(request, args, timeoutMs, cancellationToken);\n        }\n\n        async Task<RpcResponse> SendMessage(RpcRequest request, byte[] payload, int timeout = defaultTimeoutMs, CancellationToken cancellationToken = default)\n        {\n            return await SendMessage(MessageType.RpcRequest, request, payload, timeout: timeout, cancellationToken: cancellationToken).ConfigureAwait(false);\n        }\n\n        /// <summary>\n        /// Sends a message to the remote endpoint\n        /// </summary>\n        /// <param name=\"msgType\"></param>\n        /// <param name=\"request\"></param>\n        /// <param name=\"payload\"></param>\n        /// <param name=\"responseMsgId\"></param>\n        /// <param name=\"timeout\"></param>\n        /// <param name=\"cancellationToken\"></param>\n        /// <returns></returns>\n        /// <exception cref=\"ObjectDisposedException\">Thrown if this object has been disposed</exception>\n        /// <exception cref=\"InvalidOperationException\">Thrown if the underlying buffers have been closed by the channel owner</exception>\n        protected virtual Task<RpcResponse> SendMessage(MessageType msgType, RpcRequest request, byte[] payload, ulong responseMsgId = 0, int timeout = defaultTimeoutMs, CancellationToken cancellationToken = default)\n        {\n            ThrowIfDisposedOrShutdown();\n\n            var msgId = request.MsgId;\n\n            if (msgType == MessageType.RpcRequest)\n            {\n                Requests[request.MsgId] = request;\n            }\n\n            var success = false;\n            switch (this.protocolVersion)\n            {\n                case RpcProtocol.V1:\n                    success = WriteProtocolV1(msgType, msgId, payload, responseMsgId, timeout);\n                    break;\n                default:\n                    // Invalid protocol\n                    return Task.FromResult(new RpcResponse(false, null));\n            }\n\n            if (success)\n            {\n                Statistics.MessageSent(msgType, payload?.Length ?? 0);\n            }\n\n            if (success && msgType == MessageType.RpcRequest)\n            {\n                if (request != null)\n                {\n                    return request.ResponseReady.Task.TimeoutOrCancel(timeout, cancellationToken);\n                }\n                else\n                {\n                    return Task.FromResult(new RpcResponse(true, null));\n                }\n\n            }\n            else\n            {\n                RpcResponse rpcResponse = new RpcResponse(success, null);\n\n                if (request != null)\n                {\n                    request.IsSuccess = success;\n                    request.ResponseReady.SetResult(rpcResponse);\n                }\n                return Task.FromResult(rpcResponse);\n            }\n        }\n\n        bool WriteProtocolV1(MessageType msgType, ulong msgId, byte[] msg, ulong responseMsgId, int timeout)\n        {\n            if (Disposed)\n            {\n                return false;\n            }\n\n            if (WriteBuffer.ShuttingDown)\n            {\n                return false;\n            }\n\n            // Send the request packets\n            lock (lock_sendQ)\n            {\n                // Split message into correct packet size\n                int i = 0;\n                int left = msg?.Length ?? 0;\n\n                byte[] pMsg = null;\n\n                ushort totalPackets = ((msg?.Length ?? 0) == 0) ? (ushort)1 : Convert.ToUInt16(Math.Ceiling((double)msg.Length / (double)msgBufferLength));\n                ushort currentPacket = 1;\n\n                while (true)\n                {\n                    if (WriteBuffer.ShuttingDown)\n                    {\n                        return false;\n                    }\n\n                    pMsg = new byte[left > msgBufferLength ? msgBufferLength + protocolLength : left + protocolLength];\n\n                    // Writing protocol header\n                    var header = new RpcProtocolHeaderV1\n                    {\n                        MsgType = msgType,\n                        MsgId = msgId,\n                        CurrentPacket = currentPacket,\n                        TotalPackets = totalPackets,\n                        PayloadSize = msg?.Length ?? 0,\n                        ResponseId = responseMsgId\n                    };\n                    FastStructure.CopyTo(ref header, pMsg, 0);\n\n                    if (left > msgBufferLength)\n                    {\n                        // Writing payload\n                        if (msg != null && msg.Length > 0)\n                            Buffer.BlockCopy(msg, i, pMsg, protocolLength, msgBufferLength);\n\n                        left -= msgBufferLength;\n                        i += msgBufferLength;\n                    }\n                    else\n                    {\n                        // Writing last packet of payload\n                        if (msg != null && msg.Length > 0)\n                        {\n                            Buffer.BlockCopy(msg, i, pMsg, protocolLength, left);\n                        }\n\n                        left = 0;\n                    }\n\n                    Statistics.StartWaitWrite();\n                    var bytes = WriteBuffer.Write((ptr) =>\n                    {\n                        FastStructure.WriteBytes(ptr, pMsg, 0, pMsg.Length);\n                        return pMsg.Length;\n                    }, 1000);\n\n                    Statistics.WritePacket(bytes - protocolLength);\n\n                    if (left <= 0)\n                    {\n                        break;\n                    }\n                    currentPacket++;\n                }\n            }\n\n            return true;\n        }\n\n        private bool m_ReadThreadIsReading = false;\n        private object m_ReadThreadIsReadingLock = new object();\n\n        void ReadThreadV1()\n        {\n            try\n            {\n                // Work with Local Variable to prevent NPE after dispose  \n                CircularBuffer l_TempReadBuffer = ReadBuffer;\n\n                while (true && !l_TempReadBuffer.ShuttingDown)\n                {\n                    if (Interlocked.Read(ref _disposed) == 1)\n                        return;\n\n                    // Check If Reading must be stopped\n                    if (_needDisposeManagedResources)\n                    {\n                        DisposeManagedResources();\n                        return;\n                    }\n\n                    // Set Marker for Reading in Progress\n                    lock (m_ReadThreadIsReadingLock)\n                    {\n                        m_ReadThreadIsReading = true;\n                    }\n\n                    try\n                    {\n                        Statistics.StartWaitRead();\n\n                        l_TempReadBuffer.Read((ptr) =>\n                        {\n                            int readLength = 0;\n                            var header = FastStructure<RpcProtocolHeaderV1>.PtrToStructure(ptr);\n                            ptr = ptr + protocolLength;\n                            readLength += protocolLength;\n\n                            RpcRequest request = null;\n                            if (header.MsgType == MessageType.RpcResponse || header.MsgType == MessageType.ErrorInRpc)\n                            {\n                                if (!Requests.TryGetValue(header.ResponseId, out request))\n                                {\n                                    // The response received does not have a  matching message that was sent\n                                    Statistics.DiscardResponse(header.ResponseId);\n                                    return protocolLength;\n                                }\n                            }\n                            else\n                            {\n                                request = IncomingRequests.GetOrAdd(header.MsgId, new RpcRequest\n                                {\n                                    MsgId = header.MsgId\n                                });\n                            }\n\n                            int packetSize = header.PayloadSize < msgBufferLength ? header.PayloadSize :\n                                (header.CurrentPacket < header.TotalPackets ? msgBufferLength : header.PayloadSize % msgBufferLength);\n\n                            if (header.PayloadSize > 0)\n                            {\n                                if (request.Data == null)\n                                {\n                                    request.Data = new byte[header.PayloadSize];\n                                }\n\n                                int index = msgBufferLength * (header.CurrentPacket - 1);\n                                FastStructure.ReadBytes(request.Data, ptr, index, packetSize);\n                                readLength += packetSize;\n                            }\n\n                            if (header.CurrentPacket == header.TotalPackets)\n                            {\n                                if (header.MsgType == MessageType.RpcResponse || header.MsgType == MessageType.ErrorInRpc)\n                                {\n                                    Requests.TryRemove(request.MsgId, out RpcRequest removed);\n                                }\n                                else\n                                {\n                                    IncomingRequests.TryRemove(request.MsgId, out RpcRequest removed);\n                                }\n\n                                // Full message is ready\n\n                                Statistics.MessageReceived(header.MsgType, request.Data?.Length ?? 0);\n\n                                if (header.MsgType == MessageType.RpcResponse)\n                                {\n                                    request.IsSuccess = true;\n                                    request.ResponseReady.SetResult(new RpcResponse(request.IsSuccess, request.Data));\n                                }\n                                else if (header.MsgType == MessageType.ErrorInRpc)\n                                {\n                                    request.IsSuccess = false;\n                                    request.ResponseReady.SetResult(new RpcResponse(request.IsSuccess, request.Data));\n                                }\n                                else if (header.MsgType == MessageType.RpcRequest)\n                                {\n                                    // For Handling Request we create an new Task because this can take sometime\n                                    Task.Run(async () =>\n                                    {\n                                        try\n                                        {\n                                            await ProcessCallHandler(request).ConfigureAwait(false);\n                                        }\n                                        catch(Exception ex)\n                                        {\n                                            // Ignore Object Disposed and Invalid Operation Exceptions\n                                            // because the other side of the rpc buffers may \n                                            // not know if this Buffer is shutting down or disposed\n                                            if (!(ex is ObjectDisposedException || ex is InvalidOperationException))\n                                            {\n                                                throw;\n                                            }\n                                        }\n                                    });\n                                }\n                            }\n\n                            Statistics.ReadPacket(packetSize);\n\n                            return protocolLength + packetSize;\n                        }, 500);\n                    }\n                    finally\n                    {\n                        lock (m_ReadThreadIsReadingLock)\n                        {\n                            m_ReadThreadIsReading = false;\n                        }\n                    }\n                }\n            }\n            finally\n            {\n                // Make sure that Dispose ManageResource has been progressed\n                if (_needDisposeManagedResources)\n                {\n                    DisposeManagedResources();\n                }\n            }\n        }\n\n        private int _processCount = 0;\n        private object _processLock = new object();\n\n        async Task ProcessCallHandler(RpcRequest request, CancellationToken cancellationToken = default)\n        {\n            // Mark as processing\n            lock (_processLock)\n            {\n                _processCount++;\n            }\n\n            try\n            {\n\n                if (RemoteCallHandler != null)\n                {\n                    RemoteCallHandler(request.MsgId, request.Data);\n                    await SendMessage(MessageType.RpcResponse, CreateMessageRequest(), null, request.MsgId, cancellationToken: cancellationToken)\n                        .ConfigureAwait(false);\n                }\n                else if (AsyncRemoteCallHandler != null)\n                {\n                    await AsyncRemoteCallHandler(request.MsgId, request.Data).ConfigureAwait(false);\n                    await SendMessage(MessageType.RpcResponse, CreateMessageRequest(), null, request.MsgId, cancellationToken: cancellationToken)\n                        .ConfigureAwait(false);\n                }\n                else if (RemoteCallHandlerWithResult != null)\n                {\n                    var result = RemoteCallHandlerWithResult(request.MsgId, request.Data);\n                    await SendMessage(MessageType.RpcResponse, CreateMessageRequest(), result, request.MsgId, cancellationToken: cancellationToken)\n                        .ConfigureAwait(false);\n                }\n                else if (AsyncRemoteCallHandlerWithResult != null)\n                {\n                    var result = await AsyncRemoteCallHandlerWithResult(request.MsgId, request.Data)\n                        .ConfigureAwait(false);\n                    await SendMessage(MessageType.RpcResponse, CreateMessageRequest(), result, request.MsgId, cancellationToken: cancellationToken)\n                        .ConfigureAwait(false);\n                }\n            }\n            catch\n            {\n                await SendMessage(MessageType.ErrorInRpc, CreateMessageRequest(), null, request.MsgId)\n                    .ConfigureAwait(false);\n            }\n            finally\n            {\n                lock (_processLock)\n                {\n                    _processCount--;\n                }\n\n                // Make sure that ManagedResources are disposed if needed\n                if (_needDisposeManagedResources)\n                {\n                    DisposeManagedResources();\n                }\n            }\n        }\n\n        #region IDisposable\n\n        /// <summary>\n        /// Checks that the object has not been disposed, and that the underlying buffers are not shutting down.\n        /// </summary>\n        /// <exception cref=\"ObjectDisposedException\">Thrown if this object has been disposed</exception>\n        /// <exception cref=\"InvalidOperationException\">Thrown if the underlying buffers have been closed by the channel owner</exception>\n        protected void ThrowIfDisposedOrShutdown()\n        {\n            if (Disposed)\n            {\n                throw new ObjectDisposedException(\"RpcBuffer\");\n            }\n\n            if (ReadBuffer.ShuttingDown || WriteBuffer.ShuttingDown)\n            {\n                throw new InvalidOperationException(\"Channel owner has closed buffers\");\n            }\n        }\n\n        /// <summary>\n        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.\n        /// </summary>\n        public void Dispose()\n        {\n            Dispose(true);\n        }\n\n        /// <summary>\n        /// IDisposable pattern - dispose of managed/unmanaged resources\n        /// </summary>\n        /// <param name=\"disposeManagedResources\">true to dispose of managed resources as well as unmanaged.</param>\n        protected virtual void Dispose(bool disposeManagedResources)\n        {\n            if (Disposed)\n            {\n                return;\n            }\n\n            if (disposeManagedResources)\n            {\n                DisposeManagedResources();   \n            }\n        }\n\n        private bool _needDisposeManagedResources = false;\n\n        private void DisposeManagedResources()\n        {\n            lock (_processLock)\n            {\n                lock (m_ReadThreadIsReadingLock)\n                {\n                    // Check if dispose is possible otherwise\n                    // mark for dispose later\n                    if (_processCount > 0)\n                    {\n                        _needDisposeManagedResources = true;\n                        return;\n                    }\n\n                    if (m_ReadThreadIsReading)\n                    {\n                        _needDisposeManagedResources = true;\n                        return;\n                    }\n\n                    // Disconnect handle to prevent processing\n                    RemoteCallHandler = null;\n                    AsyncRemoteCallHandler = null;\n                    RemoteCallHandlerWithResult = null;\n                    AsyncRemoteCallHandlerWithResult = null;\n                    _needDisposeManagedResources = false;\n                }\n            }\n\n            // Mark as Disposed first otherwise ReadThread has NullPointerException because\n            // ReadBuffer is already null but Disposed is false\n            long l_OldValue = Interlocked.CompareExchange(ref _disposed, 1, 0);\n            \n            // Make sure that only one Thread is processing the dispose\n            if (l_OldValue != 0)\n                return;\n\n            if (WriteBuffer != null)\n            {\n                WriteBuffer.Dispose();\n                WriteBuffer = null;\n            }\n\n            if (ReadBuffer != null)\n            {\n                ReadBuffer.Dispose();\n                ReadBuffer = null;\n            }\n\n            if (masterMutex != null)\n            {\n                masterMutex.Close();\n                masterMutex.Dispose();\n                masterMutex = null;\n            }\n\n            // Mark as DisposeFinished\n            Interlocked.Exchange(ref _disposed, 2);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SharedMemory/SharedArray.cs",
    "content": "﻿// SharedMemory (File: SharedMemory\\SharedArray.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.InteropServices;\nusing System.Security.Permissions;\n\nnamespace SharedMemory\n{\n    /// <summary>\n    /// A generic fixed-length shared memory array of structures with support for simple inter-process read/write synchronisation.\n    /// </summary>\n    /// <typeparam name=\"T\">The structure type that will be stored in the elements of this fixed array buffer.</typeparam>\n#if NETFULL\n    [PermissionSet(SecurityAction.LinkDemand)]\n    [PermissionSet(SecurityAction.InheritanceDemand)]\n#endif\n    public class SharedArray<T> : BufferWithLocks, IList<T>\n            where T : struct\n    {\n        /// <summary>\n        /// Gets a 32-bit integer that represents the total number of elements in the <see cref=\"SharedArray{T}\"/>\n        /// </summary>\n        public int Length { get; private set; }\n        \n        /// <summary>\n        /// Gets or sets the element at the specified index\n        /// </summary>\n        /// <param name=\"index\">The zero-based index of the element to get or set.</param>\n        /// <returns>The element at the specified index.</returns>\n        /// <exception cref=\"ArgumentOutOfRangeException\"><paramref name=\"index\"/> is less than 0 -or- index is equal to or greater than <see cref=\"Length\"/>.</exception>\n        public T this[int index]\n        {\n            get\n            {\n                T item;\n                Read(out item, index);\n                return item;\n            }\n            set\n            {\n                Write(ref value, index);\n            }\n        }\n\n        private int _elementSize;\n\n        #region Constructors\n\n        /// <summary>\n        /// Creates the shared memory array with the name specified by <paramref name=\"name\"/>.\n        /// </summary>\n        /// <param name=\"name\">The name of the shared memory array to be created.</param>\n        /// <param name=\"length\">The number of elements to make room for within the shared memory array.</param>\n        public SharedArray(string name, int length)\n            : base(name, Marshal.SizeOf(typeof(T)) * length, true)\n        {\n            Length = length;\n            _elementSize = Marshal.SizeOf(typeof(T));\n\n            Open();\n        }\n\n        /// <summary>\n        /// Opens an existing shared memory array with the name as specified by <paramref name=\"name\"/>.\n        /// </summary>\n        /// <param name=\"name\">The name of the shared memory array to open.</param>\n        /// <exception cref=\"ArgumentOutOfRangeException\">If the shared memory location specified by <paramref name=\"name\"/> does not have a <see cref=\"SharedBuffer.BufferSize\"/> that is evenly divisible by the size of <typeparamref name=\"T\"/>.</exception>\n        public SharedArray(string name)\n            : base(name, 0, false)\n        {\n            _elementSize = Marshal.SizeOf(typeof(T));\n\n            Open();\n        }\n\n        #endregion\n\n        /// <summary>\n        /// Perform any initialisation required when opening the shared memory array\n        /// </summary>\n        /// <returns>true if successful</returns>\n        protected override bool DoOpen()\n        {\n            if (!IsOwnerOfSharedMemory)\n            {\n                if (BufferSize % _elementSize != 0)\n                    throw new ArgumentOutOfRangeException(\"name\", \"BufferSize is not evenly divisible by the size of \" + typeof(T).Name);\n\n                Length = (int)(BufferSize / _elementSize);\n            }\n            return true;\n        }\n\n        #region Writing\n\n        /// <summary>\n        /// Copy <paramref name=\"data\"/> to the shared memory array element at index <paramref name=\"index\"/>.\n        /// </summary>\n        /// <param name=\"data\">The data to be written.</param>\n        /// <param name=\"index\">The zero-based index of the element to set.</param>\n        public void Write(ref T data, int index)\n        {\n            if (index > Length - 1 || index < 0)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            base.Write(ref data, index * _elementSize);\n        }\n\n        /// <summary>\n        /// Copy the elements of the array <paramref name=\"buffer\"/> into the shared memory array starting at index <paramref name=\"startIndex\"/>.\n        /// </summary>\n        /// <param name=\"buffer\">The source array to copy elements from.</param>\n        /// <param name=\"startIndex\">The zero-based index of the shared memory array element to begin writing to.</param>\n        /// <exception cref=\"ArgumentOutOfRangeException\"><paramref name=\"startIndex\"/> is less than 0 -or- length of <paramref name=\"buffer\"/> + <paramref name=\"startIndex\"/> is greater than <see cref=\"Length\"/>.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"buffer\"/> must not be null</exception>\n        public void Write(T[] buffer, int startIndex = 0)\n        {\n            if (buffer == null)\n                throw new ArgumentNullException(\"buffer\");\n            if (buffer.Length + startIndex > Length || startIndex < 0)\n                throw new ArgumentOutOfRangeException(\"startIndex\");\n\n            base.Write(buffer, startIndex * _elementSize);\n        }\n\n        #endregion\n\n        #region Reading\n\n        /// <summary>\n        /// Reads a single element from the shared memory array into <paramref name=\"data\"/> located at <paramref name=\"index\"/>.\n        /// </summary>\n        /// <param name=\"data\">The element at the specified index.</param>\n        /// <param name=\"index\">The zero-based index of the element to get.</param>\n        /// <returns>The element at the specified index.</returns>\n        /// <exception cref=\"ArgumentOutOfRangeException\"><paramref name=\"index\"/> is less than 0 -or- index is equal to or greater than <see cref=\"Length\"/>.</exception>\n        public void Read(out T data, int index)\n        {\n            if (index > Length - 1 || index < 0)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            base.Read(out data, index * _elementSize);\n        }\n\n        /// <summary>\n        /// Reads buffer.Length elements from the shared memory array into <paramref name=\"buffer\"/> starting at the shared memory array element located at <paramref name=\"startIndex\"/>.\n        /// </summary>\n        /// <param name=\"buffer\">The destination array to copy the elements into.</param>\n        /// <param name=\"startIndex\">The zero-based index of the shared memory array element to begin reading from.</param>\n        /// <exception cref=\"ArgumentOutOfRangeException\"><paramref name=\"startIndex\"/> is less than 0 -or- length of <paramref name=\"buffer\"/> + <paramref name=\"startIndex\"/> is greater than <see cref=\"Length\"/>.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"buffer\"/> must not be null</exception>\n        public void CopyTo(T[] buffer, int startIndex = 0)\n        {\n            if (buffer == null)\n                throw new ArgumentNullException(\"buffer\");\n            if (buffer.Length + startIndex > Length || startIndex < 0)\n                throw new ArgumentOutOfRangeException(\"startIndex\");\n\n            base.Read(buffer, startIndex * _elementSize);\n        }\n\n        #endregion\n\n        #region IEnumerable<T>\n\n        /// <summary>\n        /// Returns an enumerator that iterates through the collection.\n        /// </summary>\n        /// <returns>An <see cref=\"System.Collections.Generic.IEnumerator{T}\"/> instance that can be used to iterate through the collection</returns>\n        public IEnumerator<T> GetEnumerator()\n        {\n            for (int i = 0; i < Length; i++)\n            {\n                yield return this[i];\n            }\n        }\n\n        /// <summary>\n        /// Returns an enumerator that iterates through a collection.\n        /// </summary>\n        /// <returns>An <see cref=\"System.Collections.IEnumerator\"/> object that can be used to iterate through the collection.</returns>\n        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()\n        {\n            return this.GetEnumerator();\n        }\n\n        #endregion\n\n        #region IList<T>\n        /// <summary>\n        /// Operation not supported. Throws <see cref=\"System.NotImplementedException\"/>\n        /// </summary>\n        /// <param name=\"item\"></param>\n        public void Add(T item)\n        {\n            throw new NotImplementedException();\n        }\n\n        /// <summary>\n        /// Operation not supported. Throws <see cref=\"System.NotImplementedException\"/>\n        /// </summary>\n        public void Clear()\n        {\n            throw new NotImplementedException();\n        }\n\n        /// <summary>\n        /// Checks if the list contains the specified item.\n        /// </summary>\n        /// <param name=\"item\"></param>\n        /// <returns>True if found</returns>\n        public bool Contains(T item)\n        {\n            return IndexOf(item) >= 0;\n        }\n\n        /// <summary>\n        /// Operation not supported. Throws <see cref=\"System.NotImplementedException\"/>\n        /// </summary>\n        /// <param name=\"item\"></param>\n        /// <returns></returns>\n        public bool Remove(T item)\n        {\n            throw new NotImplementedException();\n        }\n\n        /// <summary>\n        /// The number of elements in the array\n        /// </summary>\n        public int Count\n        {\n            get { return Length; }\n        }\n\n\n        /// <summary>\n        /// The elements are not read-only\n        /// </summary>\n        public bool IsReadOnly\n        {\n            get { return true; }\n        }\n\n        /// <summary>\n        /// Return the index of the specified item.\n        /// </summary>\n        /// <param name=\"item\"></param>\n        /// <returns>The index of the item if found, otherwise -1.</returns>\n        public int IndexOf(T item)\n        {\n            for (var i = 0; i < Count; i++)\n            {\n                if (this[i].Equals(item)) return i;\n            }\n            return -1;\n        }\n\n        /// <summary>\n        /// Operation not supported. Throws <see cref=\"System.NotImplementedException\"/>\n        /// </summary>\n        /// <param name=\"index\"></param>\n        /// <param name=\"item\"></param>\n        public void Insert(int index, T item)\n        {\n            throw new NotImplementedException();\n        }\n\n        /// <summary>\n        /// Operation not supported. Throws <see cref=\"System.NotImplementedException\"/>\n        /// </summary>\n        /// <param name=\"index\"></param>\n        public void RemoveAt(int index)\n        {\n            throw new NotImplementedException();\n        }\n\n        #endregion\n\n    }\n}\n"
  },
  {
    "path": "SharedMemory/SharedBuffer.cs",
    "content": "﻿// SharedMemory (File: SharedMemory\\SharedBuffer.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\n\nusing System;\nusing System.Collections.Generic;\nusing System.IO.MemoryMappedFiles;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Security.Permissions;\nusing System.Text;\nusing System.Threading;\n\nnamespace SharedMemory\n{\n    /// <summary>\n    /// Abstract base class that provides client/server support for reading/writing structures to a buffer within a <see cref=\"MemoryMappedFile\" />.\n    /// A header structure allows clients to open the buffer without knowing the size.\n    /// </summary>\n#if NETFULL\n    [PermissionSet(SecurityAction.LinkDemand)]\n    [PermissionSet(SecurityAction.InheritanceDemand)]\n#endif\n    public abstract unsafe class SharedBuffer : IDisposable\n    {\n        #region Public/Protected properties\n\n        /// <summary>\n        /// The name of the Shared Memory instance\n        /// </summary>\n        public string Name { get; private set; }\n        \n        /// <summary>\n        /// The buffer size\n        /// </summary>\n        public long BufferSize { get; private set; }\n        \n        /// <summary>\n        /// The total shared memory size, including header and buffer.\n        /// </summary>\n        public virtual long SharedMemorySize\n        {\n            get\n            {\n                return HeaderOffset + Marshal.SizeOf(typeof(SharedHeader)) + BufferSize;\n            }\n        }\n\n        /// <summary>\n        /// Indicates whether this instance owns the shared memory (i.e. creator of the shared memory)\n        /// </summary>\n        public bool IsOwnerOfSharedMemory { get; private set; }\n        \n        /// <summary>\n        /// Returns true if the SharedMemory owner has/is shutting down\n        /// </summary>\n        public bool ShuttingDown\n        {\n            get\n            {\n                if (Header == null || Header->Shutdown == 1)\n                {\n                    return true;\n                }\n                else\n                {\n                    return false;\n                }\n            }\n        }\n\n        /// <summary>\n        /// Where the header starts within the shared memory\n        /// </summary>\n        protected virtual long HeaderOffset\n        {\n            get\n            {\n                return 0;\n            }\n        }\n\n        /// <summary>\n        /// Where the buffer is located within the shared memory\n        /// </summary>\n        protected virtual long BufferOffset\n        {\n            get\n            {\n                return HeaderOffset + Marshal.SizeOf(typeof(SharedHeader));\n            }\n        }\n\n        #endregion\n\n        #region Protected field members\n\n        /// <summary>\n        /// Memory mapped file\n        /// </summary>\n        protected MemoryMappedFile Mmf;\n        /// <summary>\n        /// Memory mapped view\n        /// </summary>\n        protected MemoryMappedViewAccessor View;\n        /// <summary>\n        /// Pointer to the memory mapped view\n        /// </summary>\n        protected byte* ViewPtr = null;\n        /// <summary>\n        /// Pointer to the start of the buffer region of the memory mapped view\n        /// </summary>\n        protected byte* BufferStartPtr = null;\n        /// <summary>\n        /// Pointer to the header within shared memory\n        /// </summary>\n        protected SharedHeader* Header = null;\n\n        #endregion\n\n        #region Constructor / destructor\n\n        /// <summary>\n        /// Create a new <see cref=\"SharedBuffer\"/> instance with the specified name and buffer size\n        /// </summary>\n        /// <param name=\"name\">The name of the shared memory</param>\n        /// <param name=\"bufferSize\">The buffer size in bytes. The total shared memory size will be <code>Marshal.SizeOf(SharedMemory.SharedHeader) + bufferSize</code></param>\n        /// <param name=\"ownsSharedMemory\">Whether or not the current instance owns the shared memory. If true a new shared memory will be created and initialised otherwise an existing one is opened.</param>\n        /// <remarks>\n        /// <para>The maximum total shared memory size is dependent upon the system and current memory fragmentation.</para>\n        /// <para>The shared memory layout on 32-bit and 64-bit is:<br />\n        /// <code>\n        /// |       Header       |    Buffer    |<br />\n        /// |      16-bytes      |  bufferSize  |\n        /// </code>\n        /// </para>\n        /// </remarks>\n        protected SharedBuffer(string name, long bufferSize, bool ownsSharedMemory)\n        {\n            #region Argument validation\n            if (name == String.Empty || name == null)\n                throw new ArgumentException(\"Cannot be String.Empty or null\", \"name\");\n            if (ownsSharedMemory && bufferSize <= 0)\n                throw new ArgumentOutOfRangeException(\"bufferSize\", bufferSize, \"Buffer size must be larger than zero when creating a new shared memory buffer.\");\n#if DEBUG\n            else if (!ownsSharedMemory && bufferSize > 0)\n                System.Diagnostics.Debug.Write(\"Buffer size is ignored when opening an existing shared memory buffer.\", \"Warning\");\n#endif\n            #endregion\n\n            IsOwnerOfSharedMemory = ownsSharedMemory;\n            Name = name;\n\n            if (IsOwnerOfSharedMemory)\n            {\n                BufferSize = bufferSize;\n            }\n        }\n\n        /// <summary>\n        /// Destructor - for Dispose(false)\n        /// </summary>\n        ~SharedBuffer()\n        {\n            Dispose(false);\n        }\n\n        #endregion\n\n        #region Open / Close\n\n        /// <summary>\n        /// Creates a new or opens an existing shared memory buffer with the name of <see cref=\"Name\"/> depending on the value of <see cref=\"IsOwnerOfSharedMemory\"/>. \n        /// </summary>\n        /// <returns>True if the memory was successfully mapped</returns>\n        /// <remarks>If <see cref=\"IsOwnerOfSharedMemory\"/> is true then the shared memory buffer will be created, opening will fail in this case if the shared memory already exists. Otherwise if IsOwnerOfSharedMemory is false then the shared memory buffer will be opened, which will fail if it doesn't already exist.</remarks>\n        /// <exception cref=\"System.IO.IOException\">If trying to create a new shared memory buffer with a duplicate name as buffer owner.</exception>\n        /// <exception cref=\"System.IO.FileNotFoundException\">If trying to open a new shared memory buffer that does not exist as a consumer of existing buffer.</exception>\n        /// <exception cref=\"System.ArgumentOutOfRangeException\">If trying to create a new shared memory buffer with a size larger than the logical addressable space.</exception>\n        [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Usage\", \"CA2202:Do not dispose objects multiple times\")]\n        protected bool Open()\n        {\n            Close();\n\n            try\n            {\n                // Attempts to create or open the shared memory with a name of this.Name\n                if (IsOwnerOfSharedMemory)\n                {\n                    // Create a new shared memory mapping\n                    Mmf = MemoryMappedFile.CreateNew(Name, SharedMemorySize);\n\n                    // Create a view to the entire region of the shared memory\n                    View = Mmf.CreateViewAccessor(0, SharedMemorySize, MemoryMappedFileAccess.ReadWrite);\n                    View.SafeMemoryMappedViewHandle.AcquirePointer(ref ViewPtr);\n                    Header = (SharedHeader*)(ViewPtr + HeaderOffset);\n                    BufferStartPtr = ViewPtr + BufferOffset;\n                    // Initialise the header\n                    InitialiseHeader();\n                }\n                else\n                {\n                    // Open an existing shared memory mapping\n                    Mmf = MemoryMappedFile.OpenExisting(Name);\n\n                    // Retrieve the header from the shared memory in order to initialise the correct size\n                    using (var headerView = Mmf.CreateViewAccessor(0, HeaderOffset + Marshal.SizeOf(typeof(SharedHeader)), MemoryMappedFileAccess.Read))\n                    {\n                        byte* headerPtr = null;\n                        headerView.SafeMemoryMappedViewHandle.AcquirePointer(ref headerPtr);\n                        var header = (SharedHeader*)(headerPtr + HeaderOffset);\n                        BufferSize = header->SharedMemorySize - Marshal.SizeOf(typeof(SharedHeader));\n                        headerView.SafeMemoryMappedViewHandle.ReleasePointer();\n                    }\n\n                    // Create a view to the entire region of the shared memory\n                    View = Mmf.CreateViewAccessor(0, SharedMemorySize, MemoryMappedFileAccess.ReadWrite);\n                    View.SafeMemoryMappedViewHandle.AcquirePointer(ref ViewPtr);\n                    Header = (SharedHeader*)(ViewPtr + HeaderOffset);\n                    BufferStartPtr = ViewPtr + HeaderOffset + Marshal.SizeOf(typeof(SharedHeader));\n                }\n            }\n            catch\n            {\n                Close();\n                throw;\n            }\n\n            // Complete any additional open logic\n            try\n            {\n                if (!DoOpen())\n                {\n                    Close();\n                    return false;\n                }\n                else\n                {\n                    return true;\n                }\n            }\n            catch\n            {\n                Close();\n                throw;\n            }\n        }\n\n        /// <summary>\n        /// Allows any classes that inherit from <see cref=\"SharedBuffer\"/> to perform additional open logic. There is no need to call base.DoOpen() from these implementations.\n        /// </summary>\n        /// <returns>True if successful, otherwise false.</returns>\n        /// <remarks>By throwing an exception or returning false, the call to <see cref=\"Open\"/> will fail and <see cref=\"Close\"/> will be called.</remarks>\n        protected virtual bool DoOpen()\n        {\n            return true;\n        }\n\n        /// <summary>\n        /// Initialises the header within the shared memory. Only applicable if <see cref=\"IsOwnerOfSharedMemory\"/> is true.\n        /// </summary>\n        protected void InitialiseHeader()\n        {\n            if (!IsOwnerOfSharedMemory)\n                return;\n\n            SharedHeader header = new SharedHeader();\n            header.SharedMemorySize = SharedMemorySize;\n            header.Shutdown = 0;\n            View.Write<SharedHeader>(HeaderOffset, ref header);\n        }\n\n        /// <summary>\n        /// Sets the <see cref=\"ShuttingDown\"/> flag, and disposes of the MemoryMappedFile and MemoryMappedViewAccessor.<br />\n        /// Attempting to read/write to the buffer after closing will result in a <see cref=\"System.NullReferenceException\"/>.\n        /// </summary>\n        public virtual void Close()\n        {\n            if (IsOwnerOfSharedMemory && View != null)\n            {\n                // Indicates to any open instances that the owner is no longer open\n#pragma warning disable 0420 // ignore ref to volatile warning - Interlocked API\n                Interlocked.Exchange(ref Header->Shutdown, 1);\n#pragma warning restore 0420\n            }\n\n            // Allow additional close logic\n            DoClose();\n\n            // Close the MemoryMappedFile and MemoryMappedViewAccessor\n            if (View != null)\n            {\n                View.SafeMemoryMappedViewHandle.ReleasePointer();\n                View.Dispose();\n            }\n            if (Mmf != null)\n            {\n                Mmf.Dispose();\n            }\n            Header = null;\n            ViewPtr = null;\n            BufferStartPtr = null;\n            View = null;\n            Mmf = null;\n        }\n\n        /// <summary>\n        /// Any classes that inherit from <see cref=\"SharedBuffer\"/> should implement any <see cref=\"Close\"/> logic here, <see cref=\"Mmf\"/> and <see cref=\"View\"/> are still active at this point. There is no need to call base.DoClose() from these classes.\n        /// </summary>\n        /// <remarks>It is possible for <see cref=\"Close\"/> to be called before <see cref=\"Open\"/> has completed successfully, in this situation <see cref=\"DoClose\"/> should fail gracefully.</remarks>\n        protected virtual void DoClose()\n        {\n        }\n\n        #endregion\n\n        #region Writing\n\n        /// <summary>\n        /// Writes an instance of <typeparamref name=\"T\"/> into the buffer\n        /// </summary>\n        /// <typeparam name=\"T\">A structure type</typeparam>\n        /// <param name=\"source\">A reference to an instance of <typeparamref name=\"T\"/> to be written into the buffer</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to write to.</param>\n        protected virtual void Write<T>(ref T source, long bufferPosition = 0)\n            where T : struct\n        {\n            View.Write<T>(BufferOffset + bufferPosition, ref source);\n        }\n\n        /// <summary>\n        /// Writes an array of <typeparamref name=\"T\"/> into the buffer\n        /// </summary>\n        /// <typeparam name=\"T\">A structure type</typeparam>\n        /// <param name=\"source\">An array of <typeparamref name=\"T\"/> to be written. The length of this array controls the number of elements to be written.</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to write to.</param>\n        protected virtual void Write<T>(T[] source, long bufferPosition = 0)\n            where T : struct\n        {\n            Write<T>(source, 0, bufferPosition);\n        }\n\n        /// <summary>\n        /// Writes an array of <typeparamref name=\"T\"/> into the buffer\n        /// </summary>\n        /// <typeparam name=\"T\">A structure type</typeparam>\n        /// <param name=\"source\">An array of <typeparamref name=\"T\"/> to be written. The length of this array controls the number of elements to be written.</param>\n        /// <param name=\"index\">The index within the array to start writing from.</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to write to.</param>\n        protected virtual void Write<T>(T[] source, int index, long bufferPosition = 0)\n            where T : struct\n        {\n            FastStructure.WriteArray<T>((IntPtr)(BufferStartPtr + bufferPosition), source, index, source.Length - index);\n        }\n\n        /// <summary>\n        /// Writes an array of <typeparamref name=\"T\"/> into the buffer\n        /// </summary>\n        /// <typeparam name=\"T\">A structure type</typeparam>\n        /// <param name=\"source\">The source data to be written to the buffer</param>\n        /// <param name=\"index\">The start index within <paramref name=\"source\"/>.</param>\n        /// <param name=\"count\">The number of elements to write.</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to write to.</param>\n        protected virtual void WriteArray<T>(T[] source, int index, int count, long bufferPosition = 0)\n            where T : struct\n        {\n            FastStructure.WriteArray<T>((IntPtr)(BufferStartPtr + bufferPosition), source, index, count);\n        }\n\n        /// <summary>\n        /// Writes <paramref name=\"length\"/> bytes from the <paramref name=\"source\"/> into the shared memory buffer.\n        /// </summary>\n        /// <param name=\"source\">A managed pointer to the memory location to be copied into the buffer</param>\n        /// <param name=\"length\">The number of bytes to be copied</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to write to.</param>\n        protected virtual void Write(IntPtr source, int length, long bufferPosition = 0)\n        {\n#if NETCORE\n            Buffer.MemoryCopy((void*)source, BufferStartPtr + bufferPosition, BufferSize - bufferPosition, length);\n#else\n            UnsafeNativeMethods.CopyMemory(new IntPtr(BufferStartPtr + bufferPosition), source, (uint)length);\n#endif\n        }\n\n        /// <summary>\n        /// Prepares an IntPtr to the buffer position and calls <paramref name=\"writeFunc\"/> to perform the writing.\n        /// </summary>\n        /// <param name=\"writeFunc\">A function used to write to the buffer. The IntPtr parameter is a pointer to the buffer location offset by <paramref name=\"bufferPosition\"/>.</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region to start writing to.</param>\n        protected virtual void Write(Action<IntPtr> writeFunc, long bufferPosition = 0)\n        {\n            writeFunc(new IntPtr(BufferStartPtr + bufferPosition));\n        }\n\n        #endregion\n\n        #region Reading\n\n        /// <summary>\n        /// Reads an instance of <typeparamref name=\"T\"/> from the buffer\n        /// </summary>\n        /// <typeparam name=\"T\">A structure type</typeparam>\n        /// <param name=\"data\">Output parameter that will contain the value read from the buffer</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to read from.</param>\n        protected virtual void Read<T>(out T data, long bufferPosition = 0)\n            where T : struct\n        {\n            View.Read<T>(BufferOffset + bufferPosition, out data);\n        }\n\n        /// <summary>\n        /// Reads an array of <typeparamref name=\"T\"/> from the buffer.\n        /// </summary>\n        /// <typeparam name=\"T\">A structure type</typeparam>\n        /// <param name=\"destination\">Array that will contain the values read from the buffer. The length of this array controls the number of elements to read.</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to read from.</param>\n        protected virtual void Read<T>(T[] destination, long bufferPosition = 0)\n            where T : struct\n        {\n            FastStructure.ReadArray<T>(destination, (IntPtr)(BufferStartPtr + bufferPosition), 0, destination.Length);\n        }\n\n        /// <summary>\n        /// Reads a number of elements from a memory location into the provided buffer starting at the specified index.\n        /// </summary>\n        /// <typeparam name=\"T\">The structure type</typeparam>\n        /// <param name=\"destination\">The destination buffer.</param>\n        /// <param name=\"index\">The start index within <paramref name=\"destination\"/>.</param>\n        /// <param name=\"count\">The number of elements to read.</param>\n        /// <param name=\"bufferPosition\">The source offset within the buffer region of the shared memory.</param>\n        protected virtual void ReadArray<T>(T[] destination, int index, int count, long bufferPosition)\n            where T : struct\n        {\n            FastStructure.ReadArray<T>(destination, (IntPtr)(BufferStartPtr + bufferPosition), index, count);\n        }\n\n        /// <summary>\n        /// Reads <paramref name=\"length\"/> bytes into the memory location <paramref name=\"destination\"/> from the buffer region of the shared memory.\n        /// </summary>\n        /// <param name=\"destination\">A managed pointer to the memory location to copy data into from the buffer</param>\n        /// <param name=\"length\">The number of bytes to be copied</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to read from.</param>\n        protected virtual void Read(IntPtr destination, int length, long bufferPosition = 0)\n        {\n#if NETCORE\n            Buffer.MemoryCopy(BufferStartPtr + bufferPosition, (void*)destination, length, length);\n#else\n            UnsafeNativeMethods.CopyMemory(destination, new IntPtr(BufferStartPtr + bufferPosition), (uint)length);\n#endif\n        }\n\n        /// <summary>\n        /// Prepares an IntPtr to the buffer position and calls <paramref name=\"readFunc\"/> to perform the reading.\n        /// </summary>\n        /// <param name=\"readFunc\">A function used to read from the buffer. The IntPtr parameter is a pointer to the buffer offset by <paramref name=\"bufferPosition\"/>.</param>\n        /// <param name=\"bufferPosition\">The offset within the buffer region of the shared memory to read from.</param>\n        protected virtual void Read(Action<IntPtr> readFunc, long bufferPosition = 0)\n        {\n            readFunc(new IntPtr(BufferStartPtr + bufferPosition));\n        }\n\n        #endregion\n\n        #region IDisposable\n\n        /// <summary>\n        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.\n        /// </summary>\n        public void Dispose()\n        {\n            Dispose(true);\n            GC.SuppressFinalize(this);\n        }\n\n        /// <summary>\n        /// IDisposable pattern - dispose of managed/unmanaged resources\n        /// </summary>\n        /// <param name=\"disposeManagedResources\">true to dispose of managed resources as well as unmanaged.</param>\n        protected virtual void Dispose(bool disposeManagedResources)\n        {\n            if (disposeManagedResources)\n            {\n                this.Close();\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SharedMemory/SharedHeader.cs",
    "content": "﻿// SharedMemory (File: SharedMemory\\SharedHeader.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\n\nnamespace SharedMemory\n{\n    /// <summary>\n    /// A structure that is always located at the start of the shared memory in a <see cref=\"SharedBuffer\"/> instance. \n    /// This allows the shared memory to be opened by other instances without knowing its size before hand.\n    /// </summary>\n    /// <remarks>This structure is the same size on 32-bit and 64-bit architectures.</remarks>\n    [StructLayout(LayoutKind.Sequential)]\n    public struct SharedHeader\n    {\n        /// <summary>\n        /// The total size of the buffer including <see cref=\"SharedHeader\"/>, i.e. <code>BufferSize + Marshal.SizeOf(typeof(SharedMemory.SharedHeader))</code>.\n        /// </summary>\n        public long SharedMemorySize;\n\n        /// <summary>\n        /// Flag indicating whether the owner of the buffer has closed its <see cref=\"System.IO.MemoryMappedFiles.MemoryMappedFile\"/> and <see cref=\"System.IO.MemoryMappedFiles.MemoryMappedViewAccessor\"/>.\n        /// </summary>\n        public volatile int Shutdown;\n\n        /// <summary>\n        /// Pad to 16-bytes.\n        /// </summary>\n        int _padding0;\n    }\n}\n"
  },
  {
    "path": "SharedMemory/SharedMemory.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks>netstandard2.1;netstandard2.0;net47;net46;net45;net4;net35</TargetFrameworks>\n    <OutputPath>..\\bin\\$(Configuration)\\</OutputPath>\n    <DocumentationFile>..\\bin\\$(Configuration)\\$(TargetFramework)\\SharedMemory.xml</DocumentationFile>\n  </PropertyGroup>\n\n  <PropertyGroup>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n    <GeneratePackageOnBuild>false</GeneratePackageOnBuild>\n    <Company />\n    <Authors>Justin Stenning</Authors>\n    <Copyright>Copyright (c) 2020 Justin Stenning</Copyright>\n  </PropertyGroup>\n\n  <PropertyGroup Condition=\" '$(TargetFramework)' == 'netstandard2.1'\">\n    <DefineConstants>NETCORE;NETCORE3_0;NET40Plus</DefineConstants>\n  </PropertyGroup>\n\n  <PropertyGroup Condition=\" '$(TargetFramework)' == 'netstandard2.0'\">\n    <DefineConstants>NETCORE;NETCORE2_0;NET40Plus</DefineConstants>\n  </PropertyGroup>\n\n  <PropertyGroup Condition=\" '$(TargetFramework)' == 'net47'\">\n    <DefineConstants>NET45;NETFULL;NET40Plus</DefineConstants>\n  </PropertyGroup>\n\n  <PropertyGroup Condition=\" '$(TargetFramework)' == 'net46'\">\n    <DefineConstants>NET45;NETFULL;NET40Plus</DefineConstants>\n  </PropertyGroup>\n  \n  <PropertyGroup Condition=\" '$(TargetFramework)' == 'net45'\">\n    <DefineConstants>NET45;NETFULL;NET40Plus</DefineConstants>\n  </PropertyGroup>\n\n  <PropertyGroup Condition=\" '$(TargetFramework)' == 'net4'\">\n    <DefineConstants>NET40;NETFULL;NET40Plus</DefineConstants>\n  </PropertyGroup>\n\n  <PropertyGroup Condition=\" '$(TargetFramework)' == 'net35'\">\n    <DefineConstants>NET35;NETFULL</DefineConstants>\n  </PropertyGroup>\n\n  <ItemGroup Condition=\"'$(TargetFramework)' == 'netstandard2.0'\">\n    <PackageReference Include=\"System.Reflection.Emit.Lightweight\">\n      <Version>4.7.0</Version>\n    </PackageReference>\n  </ItemGroup>\n\n  <ItemGroup Condition=\"'$(TargetFramework)' == 'netstandard2.1'\">\n    <PackageReference Include=\"System.Reflection.Emit.Lightweight\">\n      <Version>4.7.0</Version>\n    </PackageReference>\n  </ItemGroup>\n\n  <ItemGroup Condition=\" '$(TargetFramework)' == 'net4' or '$(TargetFramework)' == 'net35'\">\n    <Compile Remove=\"RpcBuffer.cs\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "SharedMemory/SharedMemory.licenseheader",
    "content": "extensions: designer.cs generated.cs\nextensions: .cs .cpp .h\n// SharedMemory (File: %Project%\\%FileName%)\n// Copyright (c) %CurrentYear% Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\n\nextensions: .aspx .ascx\n<%-- \nSample license text.\n--%>\nextensions: .vb\n'Sample license text.\nextensions:  .xml .config .xsd\n<!--\nSample license text.\n-->"
  },
  {
    "path": "SharedMemory/UnsafeNativeMethods.cs",
    "content": "﻿// SharedMemory (File: SharedMemory\\UnsafeNativeMethods.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Security;\nusing System.Text;\nusing Microsoft.Win32.SafeHandles;\n\nnamespace SharedMemory\n{\n    [System.Security.SuppressUnmanagedCodeSecurity]\n    internal class UnsafeNativeMethods\n    {\n        private UnsafeNativeMethods() { }\n\n#if !NETCORE\n        /// <summary>\n        /// Allow copying memory from one IntPtr to another. Required as the <see cref=\"System.Runtime.InteropServices.Marshal.Copy(System.IntPtr, System.IntPtr[], int, int)\"/> implementation does not provide an appropriate override.\n        /// </summary>\n        /// <param name=\"dest\"></param>\n        /// <param name=\"src\"></param>\n        /// <param name=\"count\"></param>\n        [DllImport(\"kernel32.dll\", EntryPoint = \"CopyMemory\", SetLastError = false)]\n        [SecurityCritical]\n        internal static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);\n\n        [DllImport(\"kernel32.dll\", EntryPoint = \"CopyMemory\", SetLastError = false)]\n        [SecurityCritical]\n        internal static extern unsafe void CopyMemoryPtr(void* dest, void* src, uint count);\n#endif\n\n#if !NET40Plus\n\n        [DllImport(\"kernel32.dll\", BestFitMapping = false, CharSet = CharSet.Auto, ExactSpelling = false)]\n        [SecurityCritical]\n        internal static extern int FormatMessage(int dwFlags, IntPtr lpSource, int dwMessageId, int dwLanguageId, StringBuilder lpBuffer, int nSize, IntPtr va_list_arguments);\n\n        [SecurityCritical]\n        internal static string GetMessage(int errorCode)\n        {\n            StringBuilder stringBuilder = new StringBuilder(512);\n            if (UnsafeNativeMethods.FormatMessage(12800, IntPtr.Zero, errorCode, 0, stringBuilder, stringBuilder.Capacity, IntPtr.Zero) != 0)\n            {\n                return stringBuilder.ToString();\n            }\n            return string.Concat(\"UnknownError_Num \", errorCode);\n        }\n\n        [StructLayout(LayoutKind.Sequential)]\n        internal struct SYSTEM_INFO\n        {\n            internal _PROCESSOR_INFO_UNION uProcessorInfo;\n            public uint dwPageSize;\n            public IntPtr lpMinimumApplicationAddress;\n            public IntPtr lpMaximumApplicationAddress;\n            public IntPtr dwActiveProcessorMask;\n            public uint dwNumberOfProcessors;\n            public uint dwProcessorType;\n            public uint dwAllocationGranularity;\n            public ushort dwProcessorLevel;\n            public ushort dwProcessorRevision;\n        }\n\n        [StructLayout(LayoutKind.Explicit)]\n        internal struct _PROCESSOR_INFO_UNION\n        {\n            [FieldOffset(0)]\n            internal uint dwOemId;\n            [FieldOffset(0)]\n            internal ushort wProcessorArchitecture;\n            [FieldOffset(2)]\n            internal ushort wReserved;\n        }\n\n        [Flags]\n        public enum FileMapAccess : uint\n        {\n            FileMapCopy = 0x0001,\n            FileMapWrite = 0x0002,\n            FileMapRead = 0x0004,\n            FileMapAllAccess = 0x001f,\n            FileMapExecute = 0x0020,\n        }\n\n        [Flags]\n        internal enum FileMapProtection : uint\n        {\n            PageReadonly = 0x02,\n            PageReadWrite = 0x04,\n            PageWriteCopy = 0x08,\n            PageExecuteRead = 0x20,\n            PageExecuteReadWrite = 0x40,\n            SectionCommit = 0x8000000,\n            SectionImage = 0x1000000,\n            SectionNoCache = 0x10000000,\n            SectionReserve = 0x4000000,\n        }\n        /// <summary>\n        /// Cannot create a file when that file already exists.\n        /// </summary>\n        internal const int ERROR_ALREADY_EXISTS = 0xB7; // 183\n        /// <summary>\n        /// The system cannot open the file.\n        /// </summary>\n        internal const int ERROR_TOO_MANY_OPEN_FILES = 0x4; // 4\n        /// <summary>\n        /// Access is denied.\n        /// </summary>\n        internal const int ERROR_ACCESS_DENIED = 0x5; // 5\n        /// <summary>\n        /// The system cannot find the file specified.\n        /// </summary>\n        internal const int ERROR_FILE_NOT_FOUND = 0x2; // 2\n\n        [DllImport(\"kernel32.dll\", CharSet = CharSet.None, SetLastError = true)]\n        [SecurityCritical]\n        internal static extern bool CloseHandle(IntPtr handle);\n\n        [DllImport(\"kernel32.dll\", BestFitMapping = false, CharSet = CharSet.Auto, SetLastError = true, ThrowOnUnmappableChar = true)]\n        [SecurityCritical]\n        internal static extern SafeMemoryMappedFileHandle CreateFileMapping(SafeFileHandle hFile, IntPtr lpAttributes, FileMapProtection fProtect, int dwMaxSizeHi, int dwMaxSizeLo, string lpName);\n        internal static SafeMemoryMappedFileHandle CreateFileMapping(SafeFileHandle hFile, FileMapProtection flProtect, Int64 ddMaxSize, string lpName)\n        {\n            int hi = (Int32)(ddMaxSize / Int32.MaxValue);\n            int lo = (Int32)(ddMaxSize % Int32.MaxValue);\n            return CreateFileMapping(hFile, IntPtr.Zero, flProtect, hi, lo, lpName);\n        }\n\n        [DllImport(\"kernel32.dll\")]\n        internal static extern void GetSystemInfo([MarshalAs(UnmanagedType.Struct)] ref SYSTEM_INFO lpSystemInfo);\n\n        [DllImport(\"kernel32.dll\", SetLastError = true)]\n        internal static extern SafeMemoryMappedViewHandle MapViewOfFile(\n            SafeMemoryMappedFileHandle hFileMappingObject,\n            FileMapAccess dwDesiredAccess,\n            UInt32 dwFileOffsetHigh,\n            UInt32 dwFileOffsetLow,\n            UIntPtr dwNumberOfBytesToMap);\n        internal static SafeMemoryMappedViewHandle MapViewOfFile(SafeMemoryMappedFileHandle hFileMappingObject, FileMapAccess dwDesiredAccess, ulong ddFileOffset, UIntPtr dwNumberofBytesToMap)\n        {\n            uint hi = (UInt32)(ddFileOffset / UInt32.MaxValue);\n            uint lo = (UInt32)(ddFileOffset % UInt32.MaxValue);\n            return MapViewOfFile(hFileMappingObject, dwDesiredAccess, hi, lo, dwNumberofBytesToMap);\n        }\n\n        [DllImport(\"kernel32.dll\", BestFitMapping = false, CharSet = CharSet.Auto, SetLastError = true, ThrowOnUnmappableChar = true)]\n        internal static extern SafeMemoryMappedFileHandle OpenFileMapping(\n             uint dwDesiredAccess,\n             bool bInheritHandle,\n             string lpName);\n\n        [DllImport(\"kernel32.dll\", SetLastError = true)]\n        internal static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);\n\n#endif\n    }\n}\n"
  },
  {
    "path": "SharedMemory/Utilities/ArraySlice.cs",
    "content": "﻿// SharedMemory (File: SharedMemory\\Utilities\\ArraySlice.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Security.Permissions;\n\nnamespace SharedMemory.Utilities\n{\n    /// <summary>\n    /// Like <see cref=\"T:System.ArraySegment`1\"/>, but works with <see cref=\"T:System.Collections.Generic.IList`1\"/> not just an array.\n    /// </summary>\n    /// <typeparam name=\"T\">The type that is stored in the elements of the <see cref=\"T:System.Collections.Generic.IList`1\"/>.</typeparam>\n#if NETFULL\n    [PermissionSet(SecurityAction.LinkDemand)]\n    [PermissionSet(SecurityAction.InheritanceDemand)]\n#endif\n    public struct ArraySlice<T> : IList<T>\n    {\n        private readonly IList<T> _list;\n        private readonly int _offset;\n        private readonly int _count;\n\n        /// <summary>\n        /// No slicing. Just mirror the list\n        /// </summary>\n        /// <param name=\"list\">The list to be sliced.</param>\n        /// <exception cref=\"ArgumentNullException\">Thrown if <paramref name=\"list\"/> is null.</exception>\n        public ArraySlice(IList<T> list)\n        {\n            if (list == null)\n                throw new ArgumentNullException(\"list\");\n\n            _list = list;\n            _offset = 0;\n            _count = list.Count;\n        }\n\n        /// <summary>\n        /// Create a slice of a list (virtually).\n        /// </summary>\n        /// <param name=\"list\">The list to be sliced.</param>\n        /// <param name=\"offset\">The offset into <paramref name=\"list\"/> to start the slice.</param>\n        /// <param name=\"count\">The number of elements to be included in this slice.</param>\n        /// <exception cref=\"ArgumentNullException\">Thrown if <paramref name=\"list\"/> is null.</exception>\n        /// <exception cref=\"ArgumentOutOfRangeException\">Thrown if <paramref name=\"offset\"/> or <paramref name=\"count\"/> are less than zero.</exception>\n        /// <exception cref=\"ArgumentException\">Thrown if the number of elements in <paramref name=\"list\"/> - <paramref name=\"offset\"/> are not less than <paramref name=\"count\"/>.</exception>\n        public ArraySlice(IList<T> list, int offset, int count)\n        {\n            if (list == null)\n                throw new ArgumentNullException(\"list\");\n            if (offset < 0)\n                throw new ArgumentOutOfRangeException(\"offset\", \"ArgumentOutOfRange_NeedNonNegNum\");\n            if (count < 0)\n                throw new ArgumentOutOfRangeException(\"count\", \"ArgumentOutOfRange_NeedNonNegNum\");\n            if (list.Count - offset < count)\n                throw new ArgumentException(\"Argument_InvalidOffLen\");\n\n            _list = list;\n            _offset = offset;\n            _count = count;\n        }\n\n        /// <summary>\n        /// The list that is being sliced.\n        /// </summary>\n        public IList<T> List\n        {\n            get\n            {\n                return _list;\n            }\n        }\n\n        /// <summary>\n        /// The offset into the <see cref=\"T:ArraySlice`1.List\"/>.\n        /// </summary>\n        public int Offset\n        {\n            get\n            {\n                return _offset;\n            }\n        }\n\n        /// <summary>\n        /// The number of elements to be included in this slice.\n        /// </summary>\n        public int Count\n        {\n            get\n            {\n                return _count;\n            }\n        }\n\n        /// <summary>\n        /// Used to determine uniqueness.\n        /// </summary>\n        /// <returns></returns>\n        public override int GetHashCode()\n        {\n            return null == _list\n                        ? 0\n                        : _list.GetHashCode() ^ _offset ^ _count;\n        }\n\n        /// <summary>Indicates whether this instance and a specified object are equal.</summary>\n        /// <returns>true if <paramref name=\"obj\" /> and this instance are the same type and represent the same value; otherwise, false.</returns>\n        /// <param name=\"obj\">Another object to compare to. </param>\n        /// <filterpriority>2</filterpriority>\n        public override bool Equals(Object obj)\n        {\n            if (obj is ArraySlice<T>)\n                return Equals((ArraySlice<T>)obj);\n            else\n                return false;\n        }\n\n        /// <summary>Indicates whether this instance and a specified object are equal.</summary>\n        /// <param name=\"obj\"></param>\n        /// <returns></returns>\n        public bool Equals(ArraySlice<T> obj)\n        {\n            return obj._list == _list && obj._offset == _offset && obj._count == _count;\n        }\n\n        /// <summary>Indicates whether this instance and a specified object are equal.</summary>\n        /// <param name=\"a\"></param>\n        /// <param name=\"b\"></param>\n        /// <returns></returns>\n        public static bool operator ==(ArraySlice<T> a, ArraySlice<T> b)\n        {\n            return a.Equals(b);\n        }\n\n        /// <summary>Indicates whether this instance and a specified object are not equal.</summary>\n        /// <param name=\"a\"></param>\n        /// <param name=\"b\"></param>\n        /// <returns></returns>\n        public static bool operator !=(ArraySlice<T> a, ArraySlice<T> b)\n        {\n            return !(a == b);\n        }\n\n        #region IList<T>\n\n        /// <summary>Gets or sets the element at the specified index.</summary>\n        /// <returns>The element at the specified index.</returns>\n        /// <param name=\"index\">The zero-based index of the element to get or set.</param>\n        /// <exception cref=\"T:System.ArgumentOutOfRangeException\">\n        /// <paramref name=\"index\" /> is not a valid index in the <see cref=\"T:System.Collections.Generic.IList`1\" />.</exception>\n        /// <exception cref=\"T:System.NotSupportedException\">The property is set and the <see cref=\"T:System.Collections.Generic.IList`1\" /> is read-only.</exception>\n        public T this[int index]\n        {\n            get\n            {\n                if (_list == null)\n                    throw new InvalidOperationException(\"InvalidOperation_NullArray\");\n                if (index < 0 || index >= _count)\n                    throw new ArgumentOutOfRangeException(\"index\");\n\n                return _list[_offset + index];\n            }\n\n            set\n            {\n                if (_list == null)\n                    throw new InvalidOperationException(\"InvalidOperation_NullArray\");\n                if (index < 0 || index >= _count)\n                    throw new ArgumentOutOfRangeException(\"index\");\n\n                _list[_offset + index] = value;\n            }\n        }\n\n        /// <summary>Determines the index of a specific item in the <see cref=\"T:System.Collections.Generic.IList`1\" />.</summary>\n        /// <returns>The index of <paramref name=\"item\" /> if found in the list; otherwise, -1.</returns>\n        /// <param name=\"item\">The object to locate in the <see cref=\"T:System.Collections.Generic.IList`1\" />.</param>\n        public int IndexOf(T item)\n        {\n            if (_list == null)\n                throw new InvalidOperationException(\"InvalidOperation_NullArray\");\n\n            for (var i = 0; i < Count; i++)\n            {\n                if (this[i].Equals(item)) return i;\n            }\n            return -1;\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        #endregion\n\n        #region ICollection<T>\n        bool ICollection<T>.IsReadOnly\n        {\n            get\n            {\n                // the indexer setter does not throw an exception although IsReadOnly is true.\n                // This is to match the behavior of arrays.\n                return true;\n            }\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>.Contains(T item)\n        {\n            if (_list == null)\n                throw new InvalidOperationException(\"InvalidOperation_NullArray\");\n\n            return IndexOf(item) >= 0;\n        }\n\n        void ICollection<T>.CopyTo(T[] array, int arrayIndex)\n        {\n            throw new NotSupportedException();\n        }\n\n        bool ICollection<T>.Remove(T item)\n        {\n            throw new NotSupportedException();\n        }\n        #endregion\n\n        #region IEnumerable<T>\n        IEnumerator<T> IEnumerable<T>.GetEnumerator()\n        {\n            if (_list == null)\n                throw new InvalidOperationException(\"InvalidOperation_NullArray\");\n\n            return new ArraySliceEnumerator(this);\n        }\n        #endregion\n\n        #region IEnumerable\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            if (_list == null)\n                throw new InvalidOperationException(\"InvalidOperation_NullArray\");\n\n            return new ArraySliceEnumerator(this);\n        }\n        #endregion\n\n        [Serializable]\n        private sealed class ArraySliceEnumerator : IEnumerator<T>\n        {\n            private IList<T> _array;\n            private int _start;\n            private int _end;\n            private int _current;\n\n            internal ArraySliceEnumerator(ArraySlice<T> arraySlice)\n            {\n                _array = arraySlice._list;\n                _start = arraySlice._offset;\n                _end = _start + arraySlice._count;\n                _current = _start - 1;\n            }\n\n            public bool MoveNext()\n            {\n                if (_current < _end)\n                {\n                    _current++;\n                    return _current < _end;\n                }\n                return false;\n            }\n\n            public T Current\n            {\n                get\n                {\n                    if (_current < _start) throw new InvalidOperationException(\"InvalidOperation_EnumNotStarted\");\n                    if (_current >= _end) throw new InvalidOperationException(\"InvalidOperation_EnumEnded\");\n                    return _array[_current];\n                }\n            }\n\n            object IEnumerator.Current\n            {\n                get\n                {\n                    return Current;\n                }\n            }\n\n            void IEnumerator.Reset()\n            {\n                _current = _start - 1;\n            }\n\n            public void Dispose()\n            {\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SharedMemory/Utilities/ExpandingArray.cs",
    "content": "﻿using System;\r\nusing System.Collections;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\n\r\nnamespace SharedMemory.Utilities\r\n{\r\n    /// <summary>\r\n    /// This is a dynamic array like .NET's List'T, except for the following:\r\n    /// - Should be used only when growing the list incrementally.  No inserts or deletes in the middle of the list.\r\n    /// - You can specify a custom allocator, so the data can be stored in any IList'T, including a memory-mapped file.\r\n    ///       for struct types.\r\n    /// - Unlike a typical dynamic array, such as List'T, this class does not copy data and fragment the memory by\r\n    ///       leaving holes that have to be freed/reallocated.\r\n    /// </summary>\r\n    /// <typeparam name=\"T\"></typeparam>\r\n    public class ExpandingArray<T> : IList<T>\r\n    {\r\n        /// <summary>\r\n        /// Passed in allocator.  We use to this get \"memory\" where the actual data is stored.  Can be any IList'T\r\n        /// </summary>\r\n        private readonly Func<int, IList<T>> _allocator;\r\n\r\n        /// <summary>\r\n        /// Backing field for Count.  Contains the number of elements in the list from the user's perspective.\r\n        /// </summary>\r\n        private int _count;\r\n\r\n        /// <summary>\r\n        /// This is where all the allocations are stored.  The 1st bucket is a special case \r\n        /// and represents the first 3 elements of the list.\r\n        /// The 2nd bucket contains the next 4 element of the list.\r\n        /// The 3rd bucket contains the next 8 element of the list.\r\n        /// The 4th bucket contains the next 16 element of the list.\r\n        /// The 5th bucket contains the next 32 element of the list.\r\n        /// etc.\r\n        /// This way, given the index into the list, we can calculate which bucket it belongs to using log(i)/log(2)\r\n        /// This array is initially allocated to contain only one bucket.   But a hint can be passed in to the constructor\r\n        /// to pre-allocate more pubckets.  This will reduce reallocating the array as it grows.\r\n        /// </summary>\r\n        private IList<T>[] _buckets;\r\n\r\n        /// <summary>\r\n        /// The allocation is used to specify a customer allocator.\r\n        ///    It's a function that returns an IList of the indicated size.\r\n        ///    It can be as simple as something like: size => new int[size]\r\n        /// The finalCapacityHint allows us to preallocate the buckets based on what the\r\n        ///    the final capacity might be.   This does not allocate the entire list.\r\n        ///    For instance, if finalCapacityHint one-million, an array of 18 objects\r\n        ///    are allocated.\r\n        ///    Guessing a smaller number simply means that the buckets are reallocated\r\n        ///    as the array grows, causing GC churn which could otherwise be avoided.\r\n        /// </summary>\r\n        /// <param name=\"allocator\"></param>\r\n        /// <param name=\"finalCapacityHint\"></param>\r\n        public ExpandingArray(Func<int, IList<T>> allocator = null, int finalCapacityHint = 1)\r\n        {\r\n            _allocator = allocator ?? (size => new T[size]);\r\n            _buckets = new IList<T>[Math.Max(GetBucketIndex(finalCapacityHint), 1)];\r\n        }\r\n\r\n        /// <summary>\r\n        /// Given the bucket number, returns the bucket entry, which may be null if it hasn't\r\n        /// been allocated yet.\r\n        /// </summary>\r\n        /// <param name=\"bucketIndex\"></param>\r\n        /// <returns></returns>\r\n        private IList<T> GetBucket(int bucketIndex)\r\n        {\r\n            if (bucketIndex >= _buckets.Length)\r\n            {\r\n                var newBuckets = new IList<T>[_buckets.Length + 1];\r\n                _buckets.CopyTo(newBuckets, 0);\r\n                _buckets = newBuckets;\r\n            }\r\n            return _buckets[bucketIndex];\r\n        }\r\n\r\n        /// <summary>\r\n        /// Given the index into the list, determines which bucket the element resides in.\r\n        /// Made pubic for testing.\r\n        /// </summary>\r\n        /// <param name=\"index\"></param>\r\n        /// <returns></returns>\r\n        public static int GetBucketIndex(int index)\r\n        {\r\n            return Math.Max((int) (Math.Log(index + 1)/Math.Log(2)) - 1, 0);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Given the index into the list, returns the index into the bucket where the actual element resides.\r\n        /// along with the bucket itself.\r\n        /// </summary>\r\n        /// <param name=\"globalIndex\"></param>\r\n        /// <param name=\"bucket\"></param>\r\n        /// <returns></returns>\r\n        private int GetLocalIndex(int globalIndex, out IList<T> bucket)\r\n        {\r\n            var bucketIndex = GetBucketIndex(globalIndex);\r\n            bucket = GetBucket(bucketIndex) ?? (_buckets[bucketIndex] = _allocator(Math.Max(3, globalIndex + 1)));\r\n            return globalIndex - (bucketIndex > 0 ? (int)Math.Pow(2, bucketIndex + 1) - 1 : 0);\r\n        }\r\n\r\n        /// <summary>\r\n        /// IList.Add().  Add an item to the list.\r\n        /// </summary>\r\n        /// <param name=\"item\"></param>\r\n        public void Add(T item)\r\n        {\r\n            var indexNewEntry = _count;\r\n\r\n            IList<T> bucket;\r\n            var localIndex = GetLocalIndex(indexNewEntry, out bucket);\r\n            bucket[localIndex] = item;\r\n\r\n            _count = indexNewEntry + 1;\r\n        }\r\n\r\n        /// <summary>Gets or sets the element at the specified index.</summary>\r\n        /// <returns>The element at the specified index.</returns>\r\n        /// <param name=\"index\">The zero-based index of the element to get or set.</param>\r\n        /// <exception cref=\"T:System.ArgumentOutOfRangeException\">\r\n        /// <paramref name=\"index\" /> is not a valid index in the <see cref=\"T:System.Collections.Generic.IList`1\" />.</exception>\r\n        /// <exception cref=\"T:System.NotSupportedException\">The property is set and the <see cref=\"T:System.Collections.Generic.IList`1\" /> is read-only.</exception>\r\n        public T this[int index]\r\n        {\r\n            get\r\n            {\r\n                IList<T> bucket;\r\n                var localIndex = GetLocalIndex(index, out bucket);\r\n                return bucket[localIndex];\r\n            }\r\n            set\r\n            {\r\n                IList<T> bucket;\r\n                var localIndex = GetLocalIndex(index, out bucket);\r\n                bucket[localIndex] = value;\r\n            }\r\n        }\r\n\r\n        /// <summary>Removes all items from the <see cref=\"T:System.Collections.Generic.ICollection`1\" />.</summary>\r\n        /// <exception cref=\"T:System.NotSupportedException\">The <see cref=\"T:System.Collections.Generic.ICollection`1\" /> is read-only. </exception>\r\n        public void Clear()\r\n        {\r\n            _count = 0;\r\n            _buckets = new IList<T>[1];\r\n        }\r\n\r\n        /// <summary>Determines whether the <see cref=\"T:System.Collections.Generic.ICollection`1\" /> contains a specific value.</summary>\r\n        /// <returns>true if <paramref name=\"item\" /> is found in the <see cref=\"T:System.Collections.Generic.ICollection`1\" />; otherwise, false.</returns>\r\n        /// <param name=\"item\">The object to locate in the <see cref=\"T:System.Collections.Generic.ICollection`1\" />.</param>\r\n        public bool Contains(T item)\r\n        {\r\n            return IndexOf(item) >= 0;\n        }\r\n\r\n        /// <summary>Copies the elements of the <see cref=\"T:System.Collections.Generic.ICollection`1\" /> to an <see cref=\"T:System.Array\" />, starting at a particular <see cref=\"T:System.Array\" /> index.</summary>\r\n        /// <param name=\"array\">The one-dimensional <see cref=\"T:System.Array\" /> that is the destination of the elements copied from <see cref=\"T:System.Collections.Generic.ICollection`1\" />. The <see cref=\"T:System.Array\" /> must have zero-based indexing.</param>\r\n        /// <param name=\"arrayIndex\">The zero-based index in <paramref name=\"array\" /> at which copying begins.</param>\r\n        /// <exception cref=\"T:System.ArgumentNullException\">\r\n        /// <paramref name=\"array\" /> is null.</exception>\r\n        /// <exception cref=\"T:System.ArgumentOutOfRangeException\">\r\n        /// <paramref name=\"arrayIndex\" /> is less than 0.</exception>\r\n        /// <exception cref=\"T:System.ArgumentException\">The number of elements in the source <see cref=\"T:System.Collections.Generic.ICollection`1\" /> is greater than the available space from <paramref name=\"arrayIndex\" /> to the end of the destination <paramref name=\"array\" />.</exception>\r\n        public void CopyTo(T[] array, int arrayIndex)\r\n        {\r\n            for (var i = 0; i < _count; i++) array[arrayIndex + i] = this[i];\r\n        }\r\n\r\n        /// <summary>not implemented Removes the first occurrence of a specific object from the <see cref=\"T:System.Collections.Generic.ICollection`1\" />.</summary>\r\n        /// <returns>true if <paramref name=\"item\" /> was successfully removed from the <see cref=\"T:System.Collections.Generic.ICollection`1\" />; otherwise, false. This method also returns false if <paramref name=\"item\" /> is not found in the original <see cref=\"T:System.Collections.Generic.ICollection`1\" />.</returns>\r\n        /// <param name=\"item\">The object to remove from the <see cref=\"T:System.Collections.Generic.ICollection`1\" />.</param>\r\n        /// <exception cref=\"T:System.NotSupportedException\">The <see cref=\"T:System.Collections.Generic.ICollection`1\" /> is read-only.</exception>\r\n        public bool Remove(T item)\r\n        {\r\n            throw new NotImplementedException();\r\n        }\r\n\r\n        /// <summary>Gets the number of elements contained in the <see cref=\"T:System.Collections.Generic.ICollection`1\" />.</summary>\r\n        /// <returns>The number of elements contained in the <see cref=\"T:System.Collections.Generic.ICollection`1\" />.</returns>\r\n        public int Count\r\n        {\r\n            get { return _count; }\r\n        }\r\n\r\n        /// <summary>Gets a value indicating whether the <see cref=\"T:System.Collections.Generic.ICollection`1\" /> is read-only.</summary>\r\n        /// <returns>true if the <see cref=\"T:System.Collections.Generic.ICollection`1\" /> is read-only; otherwise, false.</returns>\r\n        public bool IsReadOnly\r\n        {\r\n            get { return false; }\r\n        }\r\n\r\n        /// <summary>Returns an enumerator that iterates through the collection.</summary>\r\n        /// <returns>A <see cref=\"T:System.Collections.Generic.IEnumerator`1\" /> that can be used to iterate through the collection.</returns>\r\n        public IEnumerator<T> GetEnumerator()\r\n        {\r\n            for (var i = 0; i < _count; i++) yield return this[i];\r\n        }\r\n\r\n        /// <summary>Returns an enumerator that iterates through a collection.</summary>\r\n        /// <returns>An <see cref=\"T:System.Collections.IEnumerator\" /> object that can be used to iterate through the collection.</returns>\r\n        IEnumerator IEnumerable.GetEnumerator()\r\n        {\r\n            return GetEnumerator();\r\n        }\r\n\r\n\r\n        /// <summary>Determines the index of a specific item in the <see cref=\"T:System.Collections.Generic.IList`1\" />.</summary>\r\n        /// <returns>The index of <paramref name=\"item\" /> if found in the list; otherwise, -1.</returns>\r\n        /// <param name=\"item\">The object to locate in the <see cref=\"T:System.Collections.Generic.IList`1\" />.</param>\r\n        public int IndexOf(T item)\r\n        {\r\n            for (var i = 0; i < Count; i++)\n            {\n                if (this[i].Equals(item)) return i;\n            }\n            return -1;\n        }\r\n\r\n        /// <summary>not implemented.  Inserts an item to the <see cref=\"T:System.Collections.Generic.IList`1\" /> at the specified index.</summary>\r\n        /// <param name=\"index\">The zero-based index at which <paramref name=\"item\" /> should be inserted.</param>\r\n        /// <param name=\"item\">The object to insert into the <see cref=\"T:System.Collections.Generic.IList`1\" />.</param>\r\n        /// <exception cref=\"T:System.ArgumentOutOfRangeException\">\r\n        /// <paramref name=\"index\" /> is not a valid index in the <see cref=\"T:System.Collections.Generic.IList`1\" />.</exception>\r\n        /// <exception cref=\"T:System.NotSupportedException\">The <see cref=\"T:System.Collections.Generic.IList`1\" /> is read-only.</exception>\r\n        public void Insert(int index, T item)\r\n        {\r\n            throw new NotImplementedException();\r\n        }\r\n\r\n        /// <summary>not implemented.  Removes the <see cref=\"T:System.Collections.Generic.IList`1\" /> item at the specified index.</summary>\r\n        /// <param name=\"index\">The zero-based index of the item to remove.</param>\r\n        /// <exception cref=\"T:System.ArgumentOutOfRangeException\">\r\n        /// <paramref name=\"index\" /> is not a valid index in the <see cref=\"T:System.Collections.Generic.IList`1\" />.</exception>\r\n        /// <exception cref=\"T:System.NotSupportedException\">The <see cref=\"T:System.Collections.Generic.IList`1\" /> is read-only.</exception>\r\n        public void RemoveAt(int index)\r\n        {\r\n            throw new NotImplementedException();\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "SharedMemory.Tests/ArraySliceTests.cs",
    "content": "﻿// SharedMemory (File: SharedMemoryTests\\ArraySliceTests.cs)\r\n// Copyright (c) 2014 Justin Stenning\r\n// http://spazzarama.com\r\n//\r\n// Permission is hereby granted, free of charge, to any person obtaining a copy\r\n// of this software and associated documentation files (the \"Software\"), to deal\r\n// in the Software without restriction, including without limitation the rights\r\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n// copies of the Software, and to permit persons to whom the Software is\r\n// furnished to do so, subject to the following conditions:\r\n// \r\n// The above copyright notice and this permission notice shall be included in\r\n// all copies or substantial portions of the Software.\r\n// \r\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r\n// THE SOFTWARE.\r\n//\r\n// The SharedMemory library is inspired by the following Code Project article:\r\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\r\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\r\n\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\r\nusing SharedMemory.Utilities;\r\n\r\nnamespace SharedMemoryTests\r\n{\r\n    [TestClass]\r\n    public class ArraySliceTests\r\n    {\r\n        [TestMethod]\r\n        public void ArraySlice_WorksLikeArray()\r\n        {\r\n            var a = new[] {1.0, 2.71828, 3.14, 4, 4.99999, 42, 1024};\r\n            var slicea = new ArraySlice<double>(a);\r\n            var sliceaSame = new ArraySlice<double>(a);\r\n\r\n            var b = new[] { 1.0, 2, 3, 4, 5, 99, 1024 };\r\n            var sliceb = new ArraySlice<double>(b);\r\n\r\n            Assert.AreEqual(a, slicea.List);\r\n            Assert.AreEqual(0, slicea.Offset);\r\n            Assert.AreEqual(7, slicea.Count);\r\n            Assert.IsTrue(slicea.Equals(sliceaSame));\r\n            Assert.IsTrue(slicea.Equals((object)sliceaSame));\r\n            Assert.AreEqual(sliceaSame.GetHashCode(), sliceaSame.GetHashCode());\r\n            Assert.IsTrue(slicea == sliceaSame);\r\n            Assert.IsTrue(slicea != sliceb);\r\n\r\n            Assert.IsTrue(ApproximatelyEqual(4, slicea[3]));\r\n            Assert.AreEqual(6, slicea.IndexOf(1024));\r\n            Assert.AreEqual(-1, slicea.IndexOf(1025));\r\n            Assert.IsTrue(slicea.Contains(1024));\r\n            Assert.IsFalse(slicea.Contains(1025));\r\n            Assert.IsTrue(ApproximatelyEqual(1081.85827, slicea.Sum()));\r\n\r\n            IList<double> asList = slicea;\r\n\r\n            Assert.IsTrue(ApproximatelyEqual(4, asList[3]));\r\n            Assert.AreEqual(6, asList.IndexOf(1024));\r\n            Assert.AreEqual(-1, asList.IndexOf(1025));\r\n            Assert.IsTrue(asList.Contains(1024));\r\n            Assert.IsFalse(asList.Contains(1025));\r\n            Assert.IsTrue(ApproximatelyEqual(1081.85827, asList.Sum()));\r\n        }\r\n\r\n        [TestMethod]\r\n        public void ArraySlice_TestSlice()\r\n        {\r\n            var a = new[] { 1.0, 2.71828, 3.14, 4, 4.99999, 42, 1024 };\r\n            var slicea = new ArraySlice<double>(a, 2, 3);\r\n            var sliceaSame = new ArraySlice<double>(a, 2, 3);\r\n\r\n            var b = new[] { 1.0, 2, 3, 4, 5, 99, 1024 };\r\n            var sliceb = new ArraySlice<double>(b, 2, 3);\r\n\r\n            Assert.AreEqual(a, slicea.List);\r\n            Assert.AreEqual(2, slicea.Offset);\r\n            Assert.AreEqual(3, slicea.Count);\r\n            Assert.IsTrue(slicea.Equals(sliceaSame));\r\n            Assert.IsTrue(slicea.Equals((object)sliceaSame));\r\n            Assert.AreEqual(sliceaSame.GetHashCode(), sliceaSame.GetHashCode());\r\n            Assert.IsTrue(slicea == sliceaSame);\r\n            Assert.IsTrue(slicea != sliceb);\r\n\r\n            Assert.IsTrue(ApproximatelyEqual(4.99999, slicea[2]));\r\n            Assert.AreEqual(1, slicea.IndexOf(4));\r\n            Assert.AreEqual(-1, slicea.IndexOf(1025));\r\n            Assert.IsTrue(slicea.Contains(4));\r\n            Assert.IsFalse(slicea.Contains(1025));\r\n            Assert.IsTrue(ApproximatelyEqual(12.13999, slicea.Sum()));\r\n\r\n            IList<double> asList = slicea;\r\n\r\n            Assert.IsTrue(ApproximatelyEqual(4.99999, asList[2]));\r\n            Assert.AreEqual(1, asList.IndexOf(4));\r\n            Assert.AreEqual(-1, asList.IndexOf(1025));\r\n            Assert.IsTrue(asList.Contains(4));\r\n            Assert.IsFalse(asList.Contains(1025));\r\n            Assert.IsTrue(ApproximatelyEqual(12.13999, asList.Sum()));\r\n        }\r\n\r\n\r\n\r\n        // http://stackoverflow.com/a/2411661/75129\r\n        public static bool ApproximatelyEqual(double x, double y)\r\n        {\r\n            var epsilon = Math.Max(Math.Abs(x), Math.Abs(y)) * 1E-15;\r\n            return Math.Abs(x - y) <= epsilon;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "SharedMemory.Tests/ArrayTests.cs",
    "content": "﻿// SharedMemory (File: SharedMemoryTests\\ArrayTests.cs)\r\n// Copyright (c) 2014 Justin Stenning\r\n// http://spazzarama.com\r\n//\r\n// Permission is hereby granted, free of charge, to any person obtaining a copy\r\n// of this software and associated documentation files (the \"Software\"), to deal\r\n// in the Software without restriction, including without limitation the rights\r\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n// copies of the Software, and to permit persons to whom the Software is\r\n// furnished to do so, subject to the following conditions:\r\n// \r\n// The above copyright notice and this permission notice shall be included in\r\n// all copies or substantial portions of the Software.\r\n// \r\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r\n// THE SOFTWARE.\r\n//\r\n// The SharedMemory library is inspired by the following Code Project article:\r\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\r\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\r\n\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\r\nusing SharedMemory;\r\nusing System.Runtime.InteropServices;\r\nusing System.Threading.Tasks;\r\nusing SharedMemory.Utilities;\r\n\r\nnamespace SharedMemoryTests\r\n{\r\n    [TestClass]\r\n    public class ArrayTests\r\n    {\r\n        [TestMethod]\r\n        public void Indexer_ReadWriteInteger_DataMatches()\r\n        {\r\n            var name = Guid.NewGuid().ToString();\r\n            using (var sma = new SharedArray<int>(name, 10))\r\n            {\r\n                sma[0] = 3;\r\n                sma[4] = 10;\r\n\r\n                using (var smr = new SharedArray<int>(name))\r\n                {\r\n                    Assert.AreEqual(0, smr[1], \"\");\r\n                    Assert.AreEqual(3, smr[0], \"\");\r\n                    Assert.AreEqual(10, smr[4], \"\");\r\n                }\r\n\r\n                IList<int> list = sma;\r\n                list[0] = 5;\r\n                list[4] = 55;\r\n\r\n                using (var smr = new SharedArray<int>(name))\r\n                {\r\n                    IList<int> r = smr;\r\n\r\n                    Assert.AreEqual(0, r[1], \"\");\r\n                    Assert.AreEqual(5, r[0], \"\");\r\n                    Assert.AreEqual(55, r[4], \"\");\r\n                }\r\n\r\n                list[3] = 68;\r\n                IList<int> arraySlice = new ArraySlice<int>(list, 1, 8);\r\n                arraySlice[0] = 67;\r\n\r\n                using (var smr = new SharedArray<int>(name))\r\n                {\r\n                    IList<int> r = smr;\r\n                    IList<int> rarraySlice = new ArraySlice<int>(r, 1, 8);\r\n\r\n                    Assert.AreEqual(67, rarraySlice[0], \"\");\r\n                    Assert.AreEqual(68, rarraySlice[2], \"\");\r\n                    Assert.AreEqual(55, rarraySlice[3], \"\");\r\n                }\r\n\r\n            }\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Indexer_OutOfRange_ThrowsException()\r\n        {\r\n            var name = Guid.NewGuid().ToString();\r\n            using (var sma = new SharedArray<int>(name, 10))\r\n            {\r\n                bool exceptionThrown = false;\r\n                try\r\n                {\r\n                    sma[-1] = 0;\r\n                }\r\n                catch (ArgumentOutOfRangeException)\r\n                {\r\n                    exceptionThrown = true;\r\n                }\r\n\r\n                Assert.IsTrue(exceptionThrown, \"Index of -1 should result in ArgumentOutOfRangeException\");\r\n\r\n                exceptionThrown = false;\r\n                IList<int> a = sma;\r\n                try\r\n                {\r\n                    a[-1] = 0;\r\n                }\r\n                catch (ArgumentOutOfRangeException)\r\n                {\r\n                    exceptionThrown = true;\r\n                }\r\n\r\n                Assert.IsTrue(exceptionThrown, \"Index of -1 should result in ArgumentOutOfRangeException\");\r\n\r\n                try\r\n                {\r\n                    exceptionThrown = false;\r\n                    sma[sma.Length] = 0;\r\n                }\r\n                catch (ArgumentOutOfRangeException)\r\n                {\r\n                    exceptionThrown = true;\r\n                }\r\n\r\n                Assert.IsTrue(exceptionThrown, \"Index of Length should result in ArgumentOutOfRangeException\");\r\n\r\n\r\n                try\r\n                {\r\n                    exceptionThrown = false;\r\n                    a[a.Count] = 0;\r\n                }\r\n                catch (ArgumentOutOfRangeException)\r\n                {\r\n                    exceptionThrown = true;\r\n                }\r\n\r\n                Assert.IsTrue(exceptionThrown, \"Index of Length should result in ArgumentOutOfRangeException\");\r\n            }\r\n        }\r\n\r\n        [StructLayout(LayoutKind.Sequential)]\r\n        unsafe struct MyTestStruct\r\n        {\r\n            const int MAXLENGTH = 100;\r\n\r\n            fixed char name[MAXLENGTH];\r\n\r\n            public int ValueA;\r\n\r\n            public string Name\r\n            {\r\n                get\r\n                {\r\n                    fixed (char* n = name)\r\n                    {\r\n                        return new String(n);\r\n                    }\r\n                }\r\n                set\r\n                {\r\n                    fixed (char* n = name)\r\n                    {\r\n                        int indx = 0;\r\n                        foreach (char c in value)\r\n                        {\r\n                            *(n + indx) = c;\r\n                            indx++;\r\n                            if (indx >= MAXLENGTH - 1)\r\n                                break;\r\n                        }\r\n                        *(n + indx) = '\\0';\r\n                    }\r\n                }\r\n            }\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_MyTestStruct()\r\n        {\r\n            var my = new MyTestStruct();\r\n            my.Name = \"long string long string\";\r\n            my.Name = \"short string\";\r\n            Assert.AreEqual(\"short string\", my.Name);\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Indexer_ReadWriteComplexStruct_DataMatches()\r\n        {\r\n            var name = Guid.NewGuid().ToString();\r\n            using (var sma = new SharedArray<MyTestStruct>(name, 10))\r\n            {\r\n                sma[0] = new MyTestStruct { ValueA = 3, Name = \"My Test Name\" };\r\n                sma[4] = new MyTestStruct { ValueA = 10, Name = \"My Test Name2\" };\r\n\r\n                using (var smr = new SharedArray<MyTestStruct>(name))\r\n                {\r\n                    Assert.AreEqual(0, smr[1].ValueA, \"\");\r\n                    Assert.AreEqual(3, smr[0].ValueA, \"\");\r\n                    Assert.AreEqual(\"My Test Name\", smr[0].Name, \"\");\r\n                    Assert.AreEqual(10, smr[4].ValueA, \"\");\r\n                    Assert.AreEqual(\"My Test Name2\", smr[4].Name, \"\");\r\n                }\r\n            }\r\n        }\r\n\r\n        [TestMethod]\r\n        public void CopyTo_NullArray_ThrowsException()\r\n        {\r\n            var name = Guid.NewGuid().ToString();\r\n            using (var sma = new SharedArray<int>(name, 10))\r\n            {\r\n                bool exceptionThrown = false;\r\n                try\r\n                {\r\n                    sma.CopyTo(null);\r\n                }\r\n                catch (ArgumentNullException)\r\n                {\r\n                    exceptionThrown = true;\r\n                }\r\n                Assert.IsTrue(exceptionThrown, \"null buffer should result in ArgumentNullException\");\r\n            }\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Write_NullArray_ThrowsException()\r\n        {\r\n            var name = Guid.NewGuid().ToString();\r\n            using (var sma = new SharedArray<int>(name, 10))\r\n            {\r\n                bool exceptionThrown = false;\r\n                try\r\n                {\r\n                    sma.Write(null);\r\n                }\r\n                catch (ArgumentNullException)\r\n                {\r\n                    exceptionThrown = true;\r\n                }\r\n                Assert.IsTrue(exceptionThrown, \"null buffer should result in ArgumentNullException\");\r\n            }\r\n        }\r\n\r\n        [TestMethod]\r\n        public void GetEnumerator_IterateItems_DataMatches()\r\n        {\r\n            var name = Guid.NewGuid().ToString();\r\n            Random r = new Random();\r\n            int bufSize = 1024;\r\n            byte[] data = new byte[bufSize];\r\n            byte[] readBuf = new byte[bufSize];\r\n            using (var sma = new SharedArray<byte>(name, bufSize))\r\n            {\r\n                sma.Write(data);\r\n\r\n                int value = 0;\r\n                foreach (var item in sma)\r\n                {\r\n                    Assert.AreEqual(data[value], item);\r\n                    value++;\r\n                }\r\n            }\r\n        }\r\n\r\n        [TestMethod]\r\n        public void AcquireWriteLock_ReadWrite_LocksCorrectly()\r\n        {\r\n            var name = Guid.NewGuid().ToString();\r\n            Random r = new Random();\r\n            int bufSize = 1024;\r\n            byte[] data = new byte[bufSize];\r\n            byte[] readBuf = new byte[bufSize];\r\n\r\n            bool readIsFirst = false;\r\n            bool readBlocked = false;\r\n            int syncValue = 0;\r\n\r\n            // Fill with random data\r\n            r.NextBytes(data);\r\n\r\n            using (var sma = new SharedArray<byte>(name, bufSize))\r\n            {\r\n                // Acquire write lock early\r\n                sma.AcquireWriteLock();\r\n                using (var smr = new SharedArray<byte>(name))\r\n                {\r\n                    var t1 = Task.Factory.StartNew(() =>\r\n                        {\r\n                            if (System.Threading.Interlocked.Exchange(ref syncValue, 1) == 0)\r\n                                readIsFirst = true;\r\n                            // Should block until write lock is released\r\n                            smr.AcquireReadLock();\r\n                            if (System.Threading.Interlocked.Exchange(ref syncValue, 3) == 4)\r\n                                readBlocked = true;\r\n                            smr.CopyTo(readBuf);\r\n                            smr.ReleaseReadLock();\r\n                        });\r\n\r\n                    System.Threading.Thread.Sleep(10);\r\n\r\n                    var t2 = Task.Factory.StartNew(() =>\r\n                        {\r\n                            var val = System.Threading.Interlocked.Exchange(ref syncValue, 2);\r\n                            if (val == 0)\r\n                                readIsFirst = false;\r\n                            else if (val == 3)\r\n                                readBlocked = false;\r\n                            System.Threading.Thread.Sleep(10);\r\n                            sma.Write(data);\r\n                            System.Threading.Interlocked.Exchange(ref syncValue, 4);\r\n                            sma.ReleaseWriteLock();\r\n                        });\r\n\r\n                    Task.WaitAll(t1, t2);\r\n\r\n                    Assert.IsTrue(readIsFirst, \"The read thread did not enter first.\");\r\n                    Assert.IsTrue(readBlocked, \"The read thread did not block.\");\r\n\r\n                    // Check data was written before read\r\n                    for (var i = 0; i < readBuf.Length; i++)\r\n                    {\r\n                        Assert.AreEqual(data[i], readBuf[i]);\r\n                    }\r\n                }\r\n            }\r\n        }\r\n\r\n        [TestMethod]\r\n        public void AcquireReadWriteLocks_ReadWrite_Blocks()\r\n        {\r\n            var name = Guid.NewGuid().ToString();\r\n            using (var sma = new SharedArray<byte>(name, 10))\r\n            {\r\n                using (var smr = new SharedArray<byte>(name))\r\n                {\r\n                    // Acquire write lock\r\n                    sma.AcquireWriteLock();\r\n\r\n                    // Should block (and fail to reset write signal)\r\n                    Assert.IsFalse(smr.AcquireReadLock(10));\r\n\r\n                    sma.ReleaseWriteLock();\r\n\r\n                    smr.AcquireReadLock();\r\n\r\n                    // Should block (and fail to reset read signal)\r\n                    Assert.IsFalse(sma.AcquireWriteLock(10));\r\n\r\n                    smr.ReleaseReadLock();\r\n                }\r\n            }\r\n        }\r\n\r\n        [TestMethod]\r\n        public void IList_Contains()\r\n        {\r\n            var name = Guid.NewGuid().ToString();\r\n            using (var sma = new SharedArray<int>(name, 10))\r\n            {\r\n                sma[0] = 3;\r\n                sma[4] = 10;\r\n\r\n                IList<int> a = sma;\r\n\r\n                Assert.IsTrue(a.Contains(10));\r\n                Assert.IsFalse(a.Contains(11));\r\n            }\r\n        }\r\n\r\n        [TestMethod]\r\n        public void IList_IndexOf()\r\n        {\r\n            var name = Guid.NewGuid().ToString();\r\n            using (var sma = new SharedArray<int>(name, 10))\r\n            {\r\n                sma[0] = 3;\r\n                sma[4] = 10;\r\n\r\n                IList<int> a = sma;\r\n\r\n                Assert.AreEqual(4, a.IndexOf(10));\r\n                Assert.AreEqual(-1, a.IndexOf(11));\r\n            }\r\n        }\r\n\r\n        [TestMethod]\r\n        public void IList_IsReadOnly()\r\n        {\r\n            var name = Guid.NewGuid().ToString();\r\n            using (var sma = new SharedArray<int>(name, 10))\r\n            {\r\n                sma[0] = 3;\r\n                sma[4] = 10;\r\n\r\n                IList<int> a = sma;\r\n                Assert.IsTrue(a.IsReadOnly);\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "SharedMemory.Tests/BufferReadWriteTests.cs",
    "content": "﻿using System;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace SharedMemoryTests\n{\n    [TestClass]\n    public class BufferReadWriteTests\n    {\n        [TestMethod]\n        public void Constructor_ProducerConsumer_Created()\n        {\n            var name = Guid.NewGuid().ToString();\n            using (var buf = new SharedMemory.BufferReadWrite(name, 1024))\n            using (var buf2 = new SharedMemory.BufferReadWrite(name))\n            {\n\n            }\n        }\n\n        [TestMethod]\n        public void ReadWrite_Bytes_DataMatches()\n        {\n            var name = Guid.NewGuid().ToString();\n            Random r = new Random();\n            byte[] data = new byte[1024];\n            byte[] readData = new byte[1024];\n            r.NextBytes(data);\n\n            using (var buf = new SharedMemory.BufferReadWrite(name, 1024))\n            using (var buf2 = new SharedMemory.BufferReadWrite(name))\n            {\n                buf.Write(data);\n                buf2.Read(readData);\n\n                for (var i = 0; i < data.Length; i++)\n                {\n                    Assert.AreEqual(data[i], readData[i]);\n                }\n            }\n        }\n\n        [TestMethod]\n        public void ReadWrite_TimeoutException()\n        {\n            bool timedout = false;\n            var name = Guid.NewGuid().ToString();\n            byte[] data = new byte[1024];\n            byte[] readData = new byte[1024];\n\n\n            using (var buf = new SharedMemory.BufferReadWrite(name, 1024))\n            using (var buf2 = new SharedMemory.BufferReadWrite(name))\n            {\n                // Set a small timeout to speed up the test\n                buf2.ReadWriteTimeout = 0;\n\n                // Introduce possible deadlock by acquiring without releasing the write lock.\n                buf.AcquireWriteLock();\n\n                // We want the AcquireReadLock to fail\n                if (!buf2.AcquireReadLock(1))\n                {\n                    try\n                    {\n                        // Read should timeout with TimeoutException because buf.ReleaseWriteLock has not been called\n                        buf2.Read(readData);\n                    }\n                    catch (TimeoutException e)\n                    {\n                        timedout = true;\n                    }\n                }\n                Assert.AreEqual(true, timedout, \"The TimeoutException was not thrown.\");\n\n                // Remove the deadlock situation, by releasing the write lock...\n                buf.ReleaseWriteLock();\n                // ...and ensure that we can now read the data\n                if (buf.AcquireReadLock(1))\n                    buf2.Read(readData);\n                else\n                    Assert.Fail(\"Failed to acquire read lock after releasing write lock.\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SharedMemory.Tests/CircularBufferTests.cs",
    "content": "﻿// SharedMemory (File: SharedMemoryTests\\CircularBufferTests.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\n\nusing System;\nusing System.Linq;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\nusing System.Threading.Tasks;\nusing System.Runtime.InteropServices;\nusing System.Threading;\nusing SharedMemory;\n\nnamespace SharedMemoryTests\n{\n    [TestClass]\n    public class CircularBufferTests\n    {\n        #region Constructor tests\n\n        [TestMethod]\n        public void Constructor_ProducerEmptyName_ExceptionThrown()\n        {\n            string name = String.Empty;\n            try\n            {\n                using (var smr = new CircularBuffer(name, 2, 1))\n                {\n                    // Allowed String.Empty name\n                    Assert.Fail();\n                }\n            }\n            catch (ArgumentException ae)\n            {\n                Assert.AreEqual(ae.ParamName, \"name\");\n                return;\n            }\n        }\n\n        //[TestMethod]\n        //public void Constructor_ConsumerNodeCount1_ValueIgnored()\n        //{\n        //    string name = Guid.NewGuid().ToString();\n        //    using (var smr = new CircularBuffer(name, 1, 0, false))\n        //    {\n        //        Assert.AreEqual(0, smr.NodeCount);\n        //        Assert.AreEqual(0, smr.BufferSize);\n        //    }\n        //}\n\n        //[TestMethod]\n        //public void Constructor_ConsumerBufferSize1_ValueIgnored()\n        //{\n        //    string name = Guid.NewGuid().ToString();\n        //    using (var smr = new CircularBuffer(name, 0, 1, false))\n        //    {\n        //        Assert.AreEqual(0, smr.BufferSize);\n        //    }\n        //}\n\n        [TestMethod]\n        public void Constructor_ProducerNodeCount1_ExceptionThrown()\n        {\n            string name = Guid.NewGuid().ToString();\n            try\n            {\n                using (var smr = new CircularBuffer(name, 1, 1))\n                {\n                    // Allowed single element circular buffer\n                    Assert.Fail();\n                }\n            }\n            catch (ArgumentOutOfRangeException aor)\n            {\n                Assert.AreEqual(aor.ParamName, \"nodeCount\");\n                return;\n            }\n        }\n\n        [TestMethod]\n        public void Constructor_ProducerNodeCount0_ExceptionThrown()\n        {\n            string name = Guid.NewGuid().ToString();\n            try\n            {\n                using (var smr = new CircularBuffer(name, 0, 1))\n                {\n                    // Allowed zero element circular buffer\n                    Assert.Fail();\n                }\n            }\n            catch (ArgumentOutOfRangeException aor)\n            {\n                Assert.AreEqual(aor.ParamName, \"nodeCount\");\n                return;\n            }\n        }\n\n        #endregion\n\n        #region Open/Close tests\n\n        [TestMethod]\n        public void Constructor_Producer_True()\n        {\n            string name = Guid.NewGuid().ToString();\n            using (var smr = new CircularBuffer(name, 2, 1))\n            {\n            }\n        }\n\n        [TestMethod]\n        public void Constructor_ConsumerWithoutProducer_FileNotFoundException()\n        {\n            string name = Guid.NewGuid().ToString();\n            try\n            {\n                using (var smr = new CircularBuffer(name))\n                {\n                }\n            }\n            catch (System.IO.FileNotFoundException)\n            {\n                return;\n            }\n            Assert.Fail(\"Trying to open non-existant MMF did not throw FileNotFoundException\");\n        }\n\n        [TestMethod]\n        public void Constructor_DuplicateProducer_IOException()\n        {\n            string name = Guid.NewGuid().ToString();\n            try\n            {\n                using (var smr = new CircularBuffer(name, 2, 1))\n                using (var smr2 = new CircularBuffer(name, 2, 1))\n                {\n                }\n            }\n            catch (System.IO.IOException)\n            {\n                return;\n            }\n            Assert.Fail(\"Trying to create duplicate MMF did not throw IOException\");\n        }\n\n        [TestMethod]\n        public void Constructor_ProducerAndConsumer_True()\n        {\n            string name = Guid.NewGuid().ToString();\n            using (var producer = new CircularBuffer(name, 2, 1))\n            using (var consumer = new CircularBuffer(name))\n            {\n\n            }\n        }\n\n        [TestMethod]\n        public void Close_CheckShuttingDown_True()\n        {\n            string name = Guid.NewGuid().ToString();\n            using (var producer = new CircularBuffer(name, 2, 1))\n            using (var consumer = new CircularBuffer(name))\n            {\n                producer.Close();\n\n                Assert.IsTrue(consumer.ShuttingDown);\n            }\n        }\n\n        [TestMethod]\n        public void Constructor_BufferTooLarge_ArgumentOutOfRangeException()\n        {\n            // If it's not 32-bit build, then we can't test for out-of-range.\n            // Test below always succeeds on 64-bit systems.\n\n            if (IntPtr.Size != sizeof(int)) return;\n\n            string name = Guid.NewGuid().ToString();\n            try\n            {\n                using (var smr = new CircularBuffer(name, 6, int.MaxValue))\n                {\n                }\n            }\n            catch (System.IO.IOException)\n            {\n                // Success (insufficient resources)\n                return;\n            }\n            catch (ArgumentOutOfRangeException)\n            {\n                // Success\n                return;\n            }\n            Assert.Fail(\"Opening memory mapped file did not throw ArgumentOutOfRangeException for large memory buffer.\");\n\n        }\n\n        #endregion\n\n        #region Size assumption tests\n\n        [TestMethod]\n        public void StructSize_SharedMemoryHeader_Is16bytes()\n        {\n            Assert.AreEqual(16, Marshal.SizeOf(typeof(SharedHeader)));\n        }\n\n        [TestMethod]\n        public void StructSize_Node_Is32bytes()\n        {\n            Assert.AreEqual(32, Marshal.SizeOf(typeof(CircularBuffer.Node)));\n        }\n\n        [TestMethod]\n        public void StructSize_SharedMemoryNodeHeader_Is24bytes()\n        {\n            Assert.AreEqual(24, Marshal.SizeOf(typeof(CircularBuffer.NodeHeader)));\n        }\n\n        #endregion\n\n        #region Read/Write tests\n\n        [TestMethod]\n        public void ReadWrite_SingleNode_DataMatches()\n        {\n            string name = Guid.NewGuid().ToString();\n            Random r = new Random();\n            int bufSize = 1024;\n            byte[] data = new byte[bufSize];\n            byte[] readBuf = new byte[bufSize];\n\n            // Fill with random data\n            r.NextBytes(data);\n            \n            using (var smr = new CircularBuffer(name, 2, bufSize))\n            {\n                Assert.AreEqual(bufSize, smr.Write(data), String.Format(\"Failed to write {0} bytes\", bufSize));\n                Assert.AreEqual(bufSize, smr.Read(readBuf), String.Format(\"Failed to read {0} bytes\", bufSize));\n\n                for (var i = 0; i < data.Length; i++)\n                    Assert.AreEqual(data[i], readBuf[i], String.Format(\"Data written does not match data read at index {0}\", i));\n\n                CircularBuffer.NodeHeader header = smr.ReadNodeHeader();\n                Assert.AreEqual(1, header.WriteStart);\n                Assert.AreEqual(1, header.WriteEnd);\n                Assert.AreEqual(1, header.ReadStart);\n                Assert.AreEqual(1, header.ReadEnd);\n            }\n        }\n\n        /// <summary>\n        /// Test that the SharedHeader is correct before, during and after a single read/write\n        /// </summary>\n        [TestMethod]\n        public void ReadWrite_SingleNode_HeaderIndexesCorrect()\n        {\n            string name = Guid.NewGuid().ToString();\n            Random r = new Random();\n            int bufSize = 1024;\n            byte[] data = new byte[bufSize];\n            byte[] readBuf = new byte[bufSize];\n            CircularBuffer.NodeHeader header;\n\n            // Fill with random data\n            r.NextBytes(data);\n\n            using (var smr = new CircularBuffer(name, 2, bufSize))\n            {\n                header = smr.ReadNodeHeader();\n                Assert.AreEqual(0, header.WriteStart, \"Initial WriteStart\");\n                Assert.AreEqual(0, header.WriteEnd, \"Intial WriteEnd\");\n                Assert.AreEqual(0, header.ReadStart, \"Initial ReadStart\");\n                Assert.AreEqual(0, header.ReadEnd, \"Initial ReadEnd\");\n\n                Assert.AreEqual(bufSize, smr.Write((ptr) => {\n                    header = smr.ReadNodeHeader();\n                    Assert.AreEqual(1, header.WriteStart, \"During single write WriteStart\");\n                    Assert.AreEqual(0, header.WriteEnd, \"During single write WriteEnd\");\n\n                    Marshal.Copy(data, 0, ptr, bufSize);\n                    return data.Length;\n                }), String.Format(\"Failed to write {0} bytes\", bufSize));\n\n                header = smr.ReadNodeHeader();\n                Assert.AreEqual(1, header.WriteStart, \"After single write WriteStart\");\n                Assert.AreEqual(1, header.WriteEnd, \"After single write WriteEnd\");\n\n                Assert.AreEqual(bufSize, smr.Read((ptr) =>\n                    {\n                        header = smr.ReadNodeHeader();\n                        Assert.AreEqual(1, header.ReadStart, \"During single read ReadStart\");\n                        Assert.AreEqual(0, header.ReadEnd, \"During single read ReadEnd\");\n\n                        Marshal.Copy(ptr, readBuf, 0, smr.NodeBufferSize);\n                        return smr.NodeBufferSize;\n                    }), String.Format(\"Failed to read {0} bytes\", bufSize));\n\n                header = smr.ReadNodeHeader();\n                Assert.AreEqual(1, header.ReadStart, \"After single read ReadStart\");\n                Assert.AreEqual(1, header.ReadEnd, \"After single read ReadEnd\");\n            }\n        }\n\n        [StructLayout(LayoutKind.Sequential)]\n        struct MyTestStruct\n        {\n            public int Prop1;\n            public int Prop2;\n            public int Prop3;\n            public int Prop4;\n        }\n\n        [TestMethod]\n        public void ReadWrite_MyTestStruct_DataMatches()\n        {\n            string name = Guid.NewGuid().ToString();\n            int nodeSize = Marshal.SizeOf(typeof(MyTestStruct));\n\n            using (var smr = new CircularBuffer(name, 2, nodeSize))\n            using (var sm2 = new CircularBuffer(name))\n            {\n                MyTestStruct obj = new MyTestStruct\n                {\n                    Prop1 = 1,\n                    Prop2 = 2,\n                    Prop3 = 3,\n                    Prop4 = 4\n                };\n\n                smr.Write(ref obj);\n\n                MyTestStruct read;\n                int bytesRead = sm2.Read(out read);\n                if (bytesRead > 0)\n                {\n                    Assert.AreEqual(FastStructure.SizeOf<MyTestStruct>(), bytesRead);\n                    Assert.AreEqual(obj, read);\n                }\n                else\n                {\n                    Assert.Fail();\n                }\n            }\n        }\n\n        [TestMethod]\n        public void ReadWrite_1000NodesIn2NodeRing_DataMatches()\n        {\n            string name = Guid.NewGuid().ToString();\n            Random r = new Random();\n            int bufSize = 32;\n            int iterations = 1000;\n            byte[][] data = new byte[iterations][];\n            byte[] readBuf = new byte[bufSize];\n            byte[] writeBuf = null;\n\n            // Fill with random data\n            for (var i = 0; i < data.Length; i++)\n            {\n                data[i] = new byte[bufSize];\n                r.NextBytes(data[i]);\n            }\n\n            using (var smr = new CircularBuffer(name, 2, bufSize))\n            {\n                for (var iteration = 0; iteration < iterations; iteration++)\n                {\n                    writeBuf = data[iteration];\n                    Assert.AreEqual(bufSize, smr.Write(writeBuf), String.Format(\"Failed to write {0} bytes\", bufSize));\n                    Assert.AreEqual(bufSize, smr.Read(readBuf), String.Format(\"Failed to read {0} bytes\", bufSize));\n\n                    for (var i = 0; i < writeBuf.Length; i++)\n                        Assert.AreEqual(writeBuf[i], readBuf[i], String.Format(\"Data written does not match data read at index {0}\", i));\n                }\n            }\n        }\n\n        private long WriteMultiple<T>(CircularBuffer smr, T[][] data, out int timeouts, int delay = 0, int timeout = 1000)\n            where T: struct\n        {\n            T[] writeBuf = null;\n            long totalBytesWritten = 0;\n            int iteration = 0;\n            timeouts = 0;\n            while (iteration < data.Length)\n            {\n                if (delay > 0)\n                    Thread.Sleep(delay);\n\n                writeBuf = data[iteration];\n                int written = smr.Write(writeBuf, timeout: timeout);\n                totalBytesWritten += written;\n                if (written == 0)\n                    timeouts++;\n                else\n                    iteration++;\n            }\n\n            return totalBytesWritten;\n        }\n\n        private long ReadMultiple<T>(CircularBuffer smr, T[][] writtenData, out int timeouts, int delay = 0, int timeout = 1000)\n            where T : struct\n        {\n            T[] readBuf = new T[writtenData[0].Length];\n            long totalBytesRead = 0;\n            int iteration = 0;\n            timeouts = 0;\n            while (iteration < writtenData.Length)\n            {\n                if (delay > 0)\n                    Thread.Sleep(delay);\n\n                int read = smr.Read(readBuf, timeout: timeout);\n                totalBytesRead += read;\n                if (read == 0)\n                    timeouts++;\n                else\n                {\n                    iteration++;\n                }\n            }\n\n            return totalBytesRead;\n        }\n\n        private long ReadMultipleWithCheck<T>(CircularBuffer smr, T[][] writtenData, out int timeouts, int delay = 0, int timeout = 1000)\n            where T: struct\n        {\n            T[] readBuf = new T[writtenData[0].Length];\n            long totalBytesRead = 0;\n            int iteration = 0;\n            timeouts = 0;\n            while (iteration < writtenData.Length)\n            {\n                int read = smr.Read(readBuf, timeout: timeout);\n                totalBytesRead += read;\n                if (read == 0)\n                    timeouts++;\n                else\n                {\n                    for (var i = 0; i < readBuf.Length; i++)\n                        Assert.AreEqual(writtenData[iteration][i], readBuf[i], String.Format(\"Data written does not match data read for iteration {0} at index {1}\", iteration, i));\n                    iteration++;\n                }\n\n                if (delay > 0)\n                    Thread.Sleep(delay);\n            }\n\n            return totalBytesRead;\n        }\n\n        [TestMethod]\n        public void ReadWriteAsync_1000NodesIn2NodeRing_DataMatches()\n        {\n            string name = Guid.NewGuid().ToString();\n            Random r = new Random();\n            int bufSize = 32;\n            int iterations = 1000;\n            byte[][] data = new byte[iterations][];\n            int timeouts = 0;\n            // Fill with random data\n            for (var i = 0; i < data.Length; i++)\n            {\n                data[i] = new byte[bufSize];\n                r.NextBytes(data[i]);\n            }\n\n            using (var producer = new CircularBuffer(name, 2, bufSize))\n            using (var consumer = new CircularBuffer(name))\n            {\n                Action writer = () =>\n                {\n                    long totalBytesWritten = WriteMultiple(producer, data, out timeouts);\n                    Assert.AreEqual(totalBytesWritten, iterations * bufSize, \"Failed to write all bytes\");\n                };\n\n                Action reader = () =>\n                {\n                    long totalBytesRead = ReadMultipleWithCheck(consumer, data, out timeouts);\n                    Assert.AreEqual(totalBytesRead, iterations * bufSize, \"Failed to read all bytes\");\n                };\n\n                Task tWriter = Task.Factory.StartNew(writer);\n                Task tReader = Task.Factory.StartNew(reader);\n\n                if (!Task.WaitAll(new Task[] { tWriter, tReader }, 5000))\n                {\n                    Assert.Fail(\"Reader or writer took too long\");\n                }\n            }\n        }\n\n        /// <summary>\n        /// Test the write node available event signal by making the writer thread wait for the reader thread. Done by simulating a slightly slower read vs write, with a low write timeout of 1ms.\n        /// </summary>\n        [TestMethod]\n        public void ReadWriteAsync_SlowReaderSmallWriterTimeout_DataMatches()\n        {\n            string name = Guid.NewGuid().ToString();\n            Random r = new Random();\n            int bufSize = 32;\n            int iterations = 10;\n            byte[][] data = new byte[iterations][];\n\n            // Fill with random data\n            for (var i = 0; i < data.Length; i++)\n            {\n                data[i] = new byte[bufSize];\n                r.NextBytes(data[i]);\n            }\n\n            using (var producer = new CircularBuffer(name, 2, bufSize))\n            using (var consumer = new CircularBuffer(name))\n            {\n                Action writer = () =>\n                {\n                    int writeTimeouts = 0;\n                    long totalBytesWritten = WriteMultiple(producer, data, out writeTimeouts, 0, 1);\n                    Assert.IsTrue(writeTimeouts > 0);\n                };\n\n                Action reader = () =>\n                {\n                    int readTimeouts = 0;\n                    long totalBytesRead = ReadMultipleWithCheck(consumer, data, out readTimeouts, 1);\n                };\n\n                Task tWriter = Task.Factory.StartNew(writer);\n                Task tReader = Task.Factory.StartNew(reader);\n\n                if (!Task.WaitAll(new Task[] { tWriter, tReader }, 5000))\n                {\n                    Assert.Fail(\"Reader or writer took too long\");\n                }\n            }\n        }\n\n        /// <summary>\n        /// Test the read data exists event signal by making the reader thread wait for the writer thread. Done by simulating a slightly slower write vs read, with a low read timeout of 1ms.\n        /// </summary>\n        [TestMethod]\n        public void ReadWriteAsync_SlowWriterSmallReaderTimeout_DataMatches()\n        {\n            string name = Guid.NewGuid().ToString();\n            Random r = new Random();\n            int bufSize = 32;\n            int iterations = 10;\n            byte[][] data = new byte[iterations][];\n\n            // Fill with random data\n            for (var i = 0; i < data.Length; i++)\n            {\n                data[i] = new byte[bufSize];\n                r.NextBytes(data[i]);\n            }\n\n            using (var producer = new CircularBuffer(name, 2, bufSize))\n            using (var consumer = new CircularBuffer(name))\n            {\n                Action writer = () =>\n                {\n                    int writeTimeouts = 0;\n                    long totalBytesWritten = WriteMultiple(producer, data, out writeTimeouts, 1);\n                };\n\n                Action reader = () =>\n                {\n                    int readTimeouts = 0;\n                    long totalBytesRead = ReadMultiple(consumer, data, out readTimeouts, 0, 1);\n                    Assert.IsTrue(readTimeouts > 0);\n                };\n\n                Task tWriter = Task.Factory.StartNew(writer);\n                Task tReader = Task.Factory.StartNew(reader);\n\n                //Task.WaitAll(tReader, tWriter);\n\n                if (!Task.WaitAll(new Task[] { tWriter, tReader }, 10000))\n                {\n                    Assert.Fail(\"Reader or writer took too long\");\n                }\n            }\n        }\n\n\n        /// <summary>\n        /// <para>Tests the integrity of the protected <see cref=\"SharedMemoryRing.PostNode\"/> and <see cref=\"SharedMemoryRing.ReturnNode\"/> functions to ensure that\n        /// nodes are made available in the same order that they were reserved regardless of the order Post/ReturnNode is called.</para>\n        /// <para>E.g. if nodes 1, 2 & 3 are reserved for writing in sequence, but they are ready in reverse order (i.e. PostNode is called for node 3, 2 and then 1), \n        /// the call to PostNode for node 3 and 2 will simply mark <see cref=\"SharedMemoryRing.Node.DoneWrite\"/> as 1 and then return, while the call to PostNode \n        /// for node 1 will move the WriteEnd pointer for node 1, 2 and then 3 also clearing the DoneWrite flag. This ensures that the nodes are ready for reading in \n        /// the correct order and the read/write indexes maintain their integrity. The same applies to reading and ReturnNode.</para>\n        /// </summary>\n        [TestMethod]\n        public void ReadWrite_NonSequentialReadWrite_HeaderIndexesCorrect()\n        {\n            string name = Guid.NewGuid().ToString();\n            Random r = new Random();\n            int bufSize = 1024;\n            byte[] data = new byte[bufSize];\n            byte[] readBuf = new byte[bufSize];\n            CircularBuffer.NodeHeader header;\n\n            // Fill with random data\n            r.NextBytes(data);\n\n            using (var smr = new CircularBuffer(name, 5, bufSize))\n            {\n                header = smr.ReadNodeHeader();\n                Assert.AreEqual(0, header.WriteStart, \"Initial WriteStart\");\n                Assert.AreEqual(0, header.WriteEnd, \"Intial WriteEnd\");\n                Assert.AreEqual(0, header.ReadStart, \"Initial ReadStart\");\n                Assert.AreEqual(0, header.ReadEnd, \"Initial ReadEnd\");\n\n                Assert.AreEqual(bufSize, smr.Write((ptr) =>\n                {\n                    header = smr.ReadNodeHeader();\n                    Assert.AreEqual(1, header.WriteStart, \"During nested out of order write (1) WriteStart\");\n                    Assert.AreEqual(0, header.WriteEnd, \"During nested out of order write (1) WriteEnd\");\n\n                    smr.Write((ptr2) =>\n                    {\n                        header = smr.ReadNodeHeader();\n                        Assert.AreEqual(2, header.WriteStart, \"During nested out of order write (2) WriteStart\");\n                        Assert.AreEqual(0, header.WriteEnd, \"During nested out of order write (2) WriteEnd\");\n\n                        smr.Write((ptr3) =>\n                        {\n                            header = smr.ReadNodeHeader();\n                            Assert.AreEqual(3, header.WriteStart, \"During nested out of order write (3) WriteStart\");\n                            Assert.AreEqual(0, header.WriteEnd, \"During nested out of order write (3) WriteEnd\");\n\n                            Marshal.Copy(data, 0, ptr3, bufSize);\n                            return bufSize;\n                        });\n                        header = smr.ReadNodeHeader();\n                        Assert.AreEqual(0, header.WriteEnd, \"After nested out of order write (3) WriteEnd\");\n\n                        Marshal.Copy(data, 0, ptr2, bufSize);\n                        return bufSize;\n                    });\n                    header = smr.ReadNodeHeader();\n                    Assert.AreEqual(0, header.WriteEnd, \"After nested out of order write (2) WriteEnd\");\n\n                    Marshal.Copy(data, 0, ptr, bufSize);\n                    return data.Length;\n                }), String.Format(\"Failed to write {0} bytes\", bufSize));\n\n                header = smr.ReadNodeHeader();\n                Assert.AreEqual(3, header.WriteStart, \"After nested out of order writes (1,2,3) WriteStart\");\n                Assert.AreEqual(3, header.WriteEnd, \"After nested out of order writes (1,2,3) WriteEnd\");\n\n                Assert.AreEqual(bufSize, smr.Read((ptr) =>\n                {\n                    header = smr.ReadNodeHeader();\n                    Assert.AreEqual(1, header.ReadStart, \"During nested out of order read (1) ReadStart\");\n                    Assert.AreEqual(0, header.ReadEnd, \"During nested out of order read (1) ReadEnd\");\n\n                    smr.Read((ptr2) =>\n                    {\n                        header = smr.ReadNodeHeader();\n                        Assert.AreEqual(2, header.ReadStart, \"During nested out of order read (2) ReadStart\");\n                        Assert.AreEqual(0, header.ReadEnd, \"During nested out of order read (2) ReadEnd\");\n\n                        smr.Read((ptr3) =>\n                        {\n                            header = smr.ReadNodeHeader();\n                            Assert.AreEqual(3, header.ReadStart, \"During nested out of order read (3) ReadStart\");\n                            Assert.AreEqual(0, header.ReadEnd, \"During nested out of order read (3) ReadEnd\");\n\n                            Marshal.Copy(ptr3, readBuf, 0, smr.NodeBufferSize);\n                            return smr.NodeBufferSize;\n                        });\n                        header = smr.ReadNodeHeader();\n                        Assert.AreEqual(0, header.ReadEnd, \"After nested out of order read (3) ReadEnd\");\n\n                        Marshal.Copy(ptr2, readBuf, 0, smr.NodeBufferSize);\n                        return smr.NodeBufferSize;\n                    });\n                    header = smr.ReadNodeHeader();\n                    Assert.AreEqual(0, header.ReadEnd, \"After nested out of order read (2) ReadEnd\");\n\n                    Marshal.Copy(ptr, readBuf, 0, smr.NodeBufferSize);\n                    return smr.NodeBufferSize;\n                }), String.Format(\"Failed to read {0} bytes\", bufSize));\n\n                header = smr.ReadNodeHeader();\n                Assert.AreEqual(3, header.ReadStart, \"After nested out of order read (1,2,3) ReadStart\");\n                Assert.AreEqual(3, header.ReadEnd, \"After nested out of order read (1,2,3) ReadEnd\");\n            }\n        }\n\n        [StructLayout(LayoutKind.Sequential)]\n        struct TestStruct\n        {\n            public int Value1;\n            public int Value2;\n        }\n\n        [TestMethod]\n        public void ReadWrite_StructuredData_DataMatches()\n        {\n            string name = Guid.NewGuid().ToString();\n            Random r = new Random();\n            TestStruct[] data = new TestStruct[100];\n            TestStruct[] readBuff = new TestStruct[100];\n            int bufSize = Marshal.SizeOf(typeof(TestStruct)) * 100;\n\n            // Fill with random data\n            for (var i = 0; i < data.Length; i++)\n            {\n                data[i].Value1 = r.Next();\n                data[i].Value2 = r.Next();\n            }\n\n            using (var smr = new CircularBuffer(name, 2, bufSize))\n            {\n                var writeCount = smr.Write(data);\n                var readCount = smr.Read(readBuff);\n\n                Assert.AreEqual(100, writeCount);\n                Assert.AreEqual(100, readCount);\n\n                for (var i = 0; i < data.Length; i++)\n                    Assert.AreEqual(data[i], readBuff[i], String.Format(\"Data written does not match data read at index {0}\", i));\n            }\n        }\n\n        [TestMethod]\n        public void ReadWrite_StructuredData_ReadWriteStartIndex()\n        {\n            string name = Guid.NewGuid().ToString();\n            Random r = new Random();\n            TestStruct[] data = new TestStruct[88];\n            TestStruct[] readBuff = new TestStruct[88];\n            \n            // Enough room to only fit 3 TestStruct elements\n            int bufSize = Marshal.SizeOf(typeof(TestStruct)) * 3;\n\n            // Fill with random data\n            for (var i = 0; i < data.Length; i++)\n            {\n                data[i].Value1 = r.Next();\n                data[i].Value2 = r.Next();\n            }\n\n            // Tests that writing to the circular buffer using startIndex will correctly write and read the entire\n            // source array, even though its size in bytes is not evenly divisible by the buffer size.\n            using (var smr = new CircularBuffer(name, 2, bufSize))\n            {\n                var totalWriteCount = 0;\n                var iterations = 0;\n                while (totalWriteCount < data.Length)\n                {\n                    var writeCount = smr.Write(data, startIndex: totalWriteCount);\n                    var readCount = smr.Read(readBuff, startIndex: totalWriteCount);\n\n                    Assert.AreEqual(writeCount, readCount);\n\n                    totalWriteCount += writeCount;\n                    iterations++;\n                }\n                Assert.AreEqual(Math.Ceiling((double)(data.Length * Marshal.SizeOf(typeof(TestStruct))) / bufSize), iterations);\n                Assert.AreEqual(data.Length, totalWriteCount);\n                for (var i = 0; i < totalWriteCount; i++)\n                    Assert.AreEqual(data[i], readBuff[i], String.Format(\"Data written does not match data read at index {0}\", i));\n            }\n        }\n\n        [TestMethod]\n        public unsafe void ReadWrite_IntPtr_DataMatches()\n        {\n            string name = Guid.NewGuid().ToString();\n            Random r = new Random();\n            int bufSize = 32;\n            int iterations = 1;\n            byte[][] data = new byte[iterations][];\n            byte[] readBuf = new byte[bufSize];\n            byte[] writeBuf = null;\n\n            // Fill with random data\n            for (var i = 0; i < data.Length; i++)\n            {\n                data[i] = new byte[bufSize];\n                r.NextBytes(data[i]);\n            }\n\n            using (var smr = new CircularBuffer(name, 2, bufSize))\n            {\n                for (var iteration = 0; iteration < iterations; iteration++)\n                {\n                    writeBuf = data[iteration];\n                    fixed (byte* wPtr = &writeBuf[0])\n                    {\n                        Assert.AreEqual(bufSize, smr.Write((IntPtr)wPtr, bufSize), String.Format(\"Failed to write {0} bytes\", bufSize));\n                    }\n                    fixed (byte* rPtr = &readBuf[0])\n                    {\n                        Assert.AreEqual(bufSize, smr.Read((IntPtr)rPtr, bufSize), String.Format(\"Failed to write {0} bytes\", bufSize));\n                    }\n\n                    for (var i = 0; i < writeBuf.Length; i++)\n                        Assert.AreEqual(writeBuf[i], readBuf[i], String.Format(\"Data written does not match data read at index {0}\", i));\n                }\n            }\n        }\n\n        [TestMethod]\n        public unsafe void ReadWrite_DelegateIntPtr_DataMatches()\n        {\n            string name = Guid.NewGuid().ToString();\n            Random r = new Random();\n            int bufSize = 32;\n            int iterations = 1;\n            byte[][] data = new byte[iterations][];\n            byte[] readBuf = new byte[bufSize];\n            byte[] writeBuf = null;\n\n            // Fill with random data\n            for (var i = 0; i < data.Length; i++)\n            {\n                data[i] = new byte[bufSize];\n                r.NextBytes(data[i]);\n            }\n\n            // create write delegate\n            Func<IntPtr,int> writeFunc = (addr) =>\n            {\n                int indx = 0;\n                int count = writeBuf.Length;\n                while (count > 0)\n                {\n                    var b = writeBuf[indx++];\n                    Marshal.WriteByte(addr, indx, b);\n                    count--;\n                }\n                return writeBuf.Length;\n            };\n\n            // create read delegate\n            Func<IntPtr, int> readFunc = (addr) =>\n            {\n                int indx = 0;\n                int count = readBuf.Length;\n                while (count > 0)\n                {\n                    readBuf[indx++] = Marshal.ReadByte(addr, indx);\n                    count--;\n                }\n                return readBuf.Length;\n            };\n\n            using (var smr = new CircularBuffer(name, 2, bufSize))\n            {\n                for (var iteration = 0; iteration < iterations; iteration++)\n                {\n                    writeBuf = data[iteration];\n                    Assert.AreEqual(bufSize, smr.Write(writeFunc), String.Format(\"Failed to write {0} bytes\", bufSize));\n                    Assert.AreEqual(bufSize, smr.Read(readFunc), String.Format(\"Failed to write {0} bytes\", bufSize));\n\n                    for (var i = 0; i < writeBuf.Length; i++)\n                        Assert.AreEqual(writeBuf[i], readBuf[i], String.Format(\"Data written does not match data read at index {0}\", i));\n                }\n            }\n        }\n\n        #endregion\n\n    }\n}\n"
  },
  {
    "path": "SharedMemory.Tests/ExpandingArrayTests.cs",
    "content": "﻿using System;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\r\nusing SharedMemory.Utilities;\r\n\r\nnamespace SharedMemoryTests\r\n{\r\n    [TestClass]\r\n    public class ExpandingArrayTests\r\n    {\r\n        [TestMethod]\r\n        public void ExpandingArrayTests_GrownReport()\r\n        {\r\n            var sb = new StringBuilder();\r\n\r\n            for (int i = 0; i < 32; i++)\r\n            {\r\n                sb.AppendFormat(\"{0}/{1}\\r\\n\", i, ExpandingArray<int>.GetBucketIndex(i));\r\n            }\r\n\r\n            Assert.AreEqual(\r\n@\"0/0\r\n1/0\r\n2/0\r\n3/1\r\n4/1\r\n5/1\r\n6/1\r\n7/2\r\n8/2\r\n9/2\r\n10/2\r\n11/2\r\n12/2\r\n13/2\r\n14/2\r\n15/3\r\n16/3\r\n17/3\r\n18/3\r\n19/3\r\n20/3\r\n21/3\r\n22/3\r\n23/3\r\n24/3\r\n25/3\r\n26/3\r\n27/3\r\n28/3\r\n29/3\r\n30/3\r\n31/4\r\n\", sb.ToString());\r\n        }\r\n\r\n        [TestMethod]\r\n        public void ExpandingArrayTests_Basic()\r\n        {\r\n            var ea = new ExpandingArray<int>(size => new int[size]);\r\n            TestEArray(ea);\r\n            ea.Clear();\r\n            TestEArray(ea);\r\n        }\r\n\r\n        private static void TestEArray(ExpandingArray<int> ea)\r\n        {\r\n            Assert.AreEqual(0, ea.Count);\r\n\r\n            ea.Add(11);\r\n            Assert.AreEqual(1, ea.Count);\r\n\r\n            ea.Add(22);\r\n            Assert.AreEqual(2, ea.Count);\r\n            Assert.IsTrue(ea.Contains(11));\r\n            Assert.IsFalse(ea.Contains(222));\r\n\r\n            ea.Add(33);\r\n            ea.Add(44);\r\n            ea.Add(55);\r\n            ea.Add(66);\r\n            ea.Add(77);\r\n            ea.Add(88);\r\n            ea.Add(99);\r\n            ea.Add(1010);\r\n            ea.Add(111);\r\n            ea.Add(1212);\r\n            ea.Add(1313);\r\n            ea.Add(1414);\r\n            ea.Add(1515);\r\n            ea.Add(1616);\r\n            ea.Add(1717);\r\n\r\n            Assert.AreEqual(10403, ea.Sum());\r\n\r\n            Assert.AreEqual(6, ea.IndexOf(77));\r\n            Assert.AreEqual(77, ea[6]);\r\n            ea[6] = 777;\r\n            Assert.AreEqual(777, ea[6]);\r\n\r\n            Assert.AreEqual(11103, ea.Sum());\r\n\r\n            \r\n//            var a = new int[ea.Count + 1];\r\n//            ea.CopyTo(a, 1);\r\n//            Assert.AreEqual(\r\n//                @\"[0],\r\n//[11],\r\n//[22],\r\n//[33],\r\n//[44],\r\n//[55],\r\n//[66],\r\n//[777],\r\n//[88],\r\n//[99],\r\n//[1010],\r\n//[111],\r\n//[1212],\r\n//[1313],\r\n//[1414],\r\n//[1515],\r\n//[1616],\r\n//[1717]\r\n//\", a.Dump());\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "SharedMemory.Tests/FastStructureTests.cs",
    "content": "﻿// SharedMemory (File: SharedMemoryTests\\FastStructureTests.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// The SharedMemory library is inspired by the following Code Project article:\n//   \"Fast IPC Communication Using Shared Memory and InterlockedCompareExchange\"\n//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int\n\nusing System;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\nusing System.Runtime.InteropServices;\nusing SharedMemory;\n\nnamespace SharedMemoryTests\n{\n    [TestClass]\n    public class FastStructureTests\n    {\n        #region Test Structures\n\n        [StructLayout(LayoutKind.Sequential, Pack = 1)]\n        public unsafe struct CompatibleStructure\n        {\n            public int Integer1;\n\n            public IntPtr Pointer1;\n\n            public IntPtr Pointer2;\n\n            public IntPtr Pointer3;\n\n            public IntPtr Pointer4;\n\n            public fixed byte Contents[8];\n\n            public int Bookend;\n        }\n\n        [StructLayout(LayoutKind.Sequential)]\n        public struct IncompatibleNestedStructure\n        {\n            public int IncompatibleNestedStructure_One;\n\n            public object IncompatibleNestedStructure_Two;\n        }\n\n        [StructLayout(LayoutKind.Sequential)]\n        public struct IncompatibleNestedStructure2\n        {\n            public int IncompatibleNestedStructure_One;\n\n            public object IncompatibleNestedStructure_Two;\n\n            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512, ArraySubType = UnmanagedType.I2)]\n            public char[] Filename;\n        }\n\n        public struct HasIncompatibleStructure\n        {\n            public int HasIncompatibleStructure_One;\n\n            public IncompatibleNestedStructure HasIncompatibleStructure_Two;\n        }\n\n        [StructLayout(LayoutKind.Sequential)]\n        public struct ComplexStructure\n        {\n\n            public int FirstElement;\n\n            public CompatibleStructure Compatible;\n\n            public int FinalElement;\n        }\n\n        #endregion\n\n        [TestMethod]\n        public void FastStructure_IncompabitibleNestedType()\n        {\n            try\n            {\n                var size = FastStructure<HasIncompatibleStructure>.Size;\n            }\n            catch (TypeInitializationException e)\n            {\n                return;\n            }\n\n            Assert.Fail(\"Did not throw TypeInitializationException for incompatible nested type: IncompatibleNestedStructure.\");\n        }\n\n        [TestMethod]\n        public void FastStructure_IncompatibleStructure()\n        {\n            try\n            {\n                var size = FastStructure<IncompatibleNestedStructure2>.Size;\n            }\n            catch (TypeInitializationException e)\n            {\n                return;\n            }\n\n            Assert.Fail(\"Did not throw TypeInitializationException for incompatible type: NestedStructure2.\");\n        }\n\n        [TestMethod]\n        public void FastStructure_CompatibleStructureSize()\n        {\n            Assert.AreEqual(IntPtr.Size * 4 + 8 + (sizeof(int) * 2), FastStructure<CompatibleStructure>.Size);\n        }\n\n        [TestMethod]\n        public void FastStructure_ComplexStructureSize()\n        {\n            var sizeOfCompatibleStructure = IntPtr.Size * 4 + 8 + (sizeof(int) * 2);\n            var sizeOfComplexStructure = (sizeof(int) * 2) + sizeOfCompatibleStructure;\n            Assert.AreEqual(sizeOfComplexStructure, FastStructure<ComplexStructure>.Size);\n        }\n\n        [TestMethod]\n        public void FastStructure_AllocHGlobalReadWrite()\n        {\n            IntPtr mem = Marshal.AllocHGlobal(FastStructure.SizeOf<ComplexStructure>());\n\n            ComplexStructure n = new ComplexStructure();\n\n            n.Compatible.Integer1 = 1;\n            n.Compatible.Bookend = 2;\n\n            n.FirstElement = 3;\n            n.FinalElement = 9;\n            unsafe\n            {\n                n.Compatible.Contents[0] = 4;\n                n.Compatible.Contents[7] = 5;\n            }\n\n            FastStructure.StructureToPtr(ref n, mem);\n\n            // Assert that the reading and writing result in same structure\n            ComplexStructure m = FastStructure.PtrToStructure<ComplexStructure>(mem);\n            Assert.AreEqual(n, m);\n            Assert.AreEqual(n.Compatible.Integer1, m.Compatible.Integer1);\n            Assert.AreEqual(n.Compatible.Bookend, m.Compatible.Bookend);\n            unsafe\n            {\n                Assert.AreEqual(n.Compatible.Contents[0], m.Compatible.Contents[0]);\n                Assert.AreEqual(n.Compatible.Contents[7], m.Compatible.Contents[7]);\n            }\n\n            // Assert that Marshal.PtrToStructure is compatible\n            m = (ComplexStructure)Marshal.PtrToStructure(mem, typeof(ComplexStructure));\n            Assert.AreEqual(n, m);\n            Assert.AreEqual(n.Compatible.Integer1, m.Compatible.Integer1);\n            Assert.AreEqual(n.Compatible.Bookend, m.Compatible.Bookend);\n            unsafe\n            {\n                Assert.AreEqual(n.Compatible.Contents[0], m.Compatible.Contents[0]);\n                Assert.AreEqual(n.Compatible.Contents[7], m.Compatible.Contents[7]);\n            }\n\n            Marshal.FreeHGlobal(mem);\n        }\n    }\n}\n"
  },
  {
    "path": "SharedMemory.Tests/RpcBufferTests.cs",
    "content": "﻿// SharedMemory (File: SharedMemoryTests\\RpcBufferTests.cs)\n// Copyright (c) 2020 Justin Stenning\n// http://spazzarama.com\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\nusing System.Threading.Tasks;\nusing System.Runtime.InteropServices;\nusing System.Threading;\nusing SharedMemory;\nusing System.Diagnostics;\n\nnamespace SharedMemoryTests\n{\n    [TestClass]\n    public class RpcBufferTests\n    {\n        string ipcName;\n        RpcBuffer ipcMaster;\n        RpcBuffer ipcSlave;\n\n        [TestInitialize]\n        public void Initialise()\n        {\n            ipcName = \"MasterSlaveTest\" + Guid.NewGuid().ToString();\n        }\n\n        [TestCleanup]\n        public void Cleanup()\n        {\n            ipcMaster?.Dispose();\n            ipcSlave?.Dispose();\n        }\n\n        [TestMethod]\n        public void Constructor_MasterSlave_Create()\n        {\n            ipcMaster = new RpcBuffer(ipcName, (msgId, payload) =>\n            {\n            });\n            ipcSlave = new RpcBuffer(ipcName, (msgId, payload) =>\n            {\n            });\n        }\n\n        [TestMethod]\n        public void Constructor_BufferCapacityOutOfRange()\n        {\n            Assert.ThrowsException<ArgumentOutOfRangeException>(() => new RpcBuffer(ipcName, 255));\n            Assert.ThrowsException<ArgumentOutOfRangeException>(() => new RpcBuffer(ipcName, 1024*1024 + 1));\n        }\n\n        [TestMethod]\n        public void RPC_MasterCallsSlave()\n        {\n            ipcMaster = new RpcBuffer(ipcName);\n            ipcSlave = new RpcBuffer(ipcName, (msgId, payload) =>\n            {\n                Assert.IsTrue(payload != null);\n                Assert.IsTrue(payload.Length == 2);\n                // Add the two bytes together\n                return BitConverter.GetBytes((payload[0] + payload[1]));\n            });\n\n            var result = ipcMaster.RemoteRequest(new byte[] { 123, 10 });\n\n            Assert.IsTrue(result.Success);\n            Assert.AreEqual(123 + 10, BitConverter.ToInt32(result.Data, 0));\n        }\n\n        [TestMethod]\n        public async Task RPC_MasterCallsSlave_Async_WithCancellationToken_WithActualCancellation()\n        {\n            ipcMaster = new RpcBuffer(ipcName);\n            var slaveBlockingTcs = new TaskCompletionSource<bool>();\n            var slaveBlockingTask = slaveBlockingTcs.Task;\n            ipcSlave = new RpcBuffer(ipcName, async (msgId, payload) =>\n            {\n                await slaveBlockingTask;\n                return BitConverter.GetBytes((payload[0] + payload[1]));\n            });\n\n            using (var cts = new CancellationTokenSource())\n            {\n                var remoteRequestTask = ipcMaster.RemoteRequestAsync(new byte[] { 123, 10 }, cancellationToken: cts.Token);\n                cts.Cancel();\n\n                if (await Task.WhenAny(remoteRequestTask, Task.Delay(TimeSpan.FromMilliseconds(50))) == remoteRequestTask)\n                {\n                    var result = await remoteRequestTask;\n                    Assert.IsFalse(result.Success);\n                }\n                else\n                {\n                    Assert.Fail(\"cancellation seems not to have worked\");\n                }\n            }\n        }\n\n        [TestMethod]\n        public async Task RPC_MasterCallsSlave_Async_WithCancellationToken_WithoutCancellation()\n        {\n            ipcMaster = new RpcBuffer(ipcName);\n            var slaveBlockingTcs = new TaskCompletionSource<bool>();\n            var slaveBlockingTask = slaveBlockingTcs.Task;\n            ipcSlave = new RpcBuffer(ipcName, async (msgId, payload) =>\n            {\n                await slaveBlockingTask;\n                return BitConverter.GetBytes((payload[0] + payload[1]));\n            });\n\n            using (var cts = new CancellationTokenSource())\n            {\n                var remoteRequestTask = ipcMaster.RemoteRequestAsync(new byte[] { 123, 10 }, cancellationToken: cts.Token);\n                slaveBlockingTcs.SetResult(false);\n\n                var result = await remoteRequestTask;\n                Assert.IsTrue(result.Success);\n            }\n        }\n\n        [TestMethod]\n        public async Task RPC_MasterCallsSlave_Sync_WithCancellationToken_WithActualCancellation()\n        {\n            ipcMaster = new RpcBuffer(ipcName);\n            var slaveBlockingTcs = new TaskCompletionSource<bool>();\n            var slaveBlockingTask = slaveBlockingTcs.Task;\n            ipcSlave = new RpcBuffer(ipcName, async (msgId, payload) =>\n            {\n                await slaveBlockingTask;\n                return BitConverter.GetBytes((payload[0] + payload[1]));\n            });\n\n            using (var cts = new CancellationTokenSource())\n            {\n                var remoteRequestTask = Task.Run(() => ipcMaster.RemoteRequest(new byte[] { 123, 10 }, cancellationToken: cts.Token));\n                cts.Cancel();\n\n                if (await Task.WhenAny(remoteRequestTask, Task.Delay(TimeSpan.FromMilliseconds(50))) == remoteRequestTask)\n                {\n                    var result = await remoteRequestTask;\n                    Assert.IsFalse(result.Success);\n                }\n                else\n                {\n                    Assert.Fail(\"cancellation seems not to have worked\");\n                }\n            }\n        }\n\n        [TestMethod]\n        public async Task RPC_MasterCallsSlave_Sync_WithCancellationToken_WithoutCancellation()\n        {\n            ipcMaster = new RpcBuffer(ipcName);\n            var slaveBlockingTcs = new TaskCompletionSource<bool>();\n            var slaveBlockingTask = slaveBlockingTcs.Task;\n            ipcSlave = new RpcBuffer(ipcName, async (msgId, payload) =>\n            {\n                await slaveBlockingTask;\n                return BitConverter.GetBytes((payload[0] + payload[1]));\n            });\n\n            using (var cts = new CancellationTokenSource())\n            {\n                var remoteRequestTask = Task.Run(() => ipcMaster.RemoteRequest(new byte[] { 123, 10 }, cancellationToken: cts.Token));\n                slaveBlockingTcs.SetResult(false);\n\n                var result = await remoteRequestTask;\n                Assert.IsTrue(result.Success);\n            }\n        }\n\n        [TestMethod]\n        public void RPC_Statistics_Reset()\n        {\n            ipcMaster = new RpcBuffer(ipcName);\n            ipcSlave = new RpcBuffer(ipcName, (msgId, payload) =>\n            {\n                Assert.IsTrue(payload != null);\n                Assert.IsTrue(payload.Length == 2);\n                // Add the two bytes together\n                return BitConverter.GetBytes((payload[0] + payload[1]));\n            });\n\n            var result = ipcMaster.RemoteRequest(new byte[] { 123, 10 });\n\n            Assert.IsTrue(result.Success);\n            Assert.AreEqual((ulong)1, ipcMaster.Statistics.RequestsSent);\n            Assert.AreEqual((ulong)1, ipcSlave.Statistics.RequestsReceived);\n            Assert.AreEqual((ulong)1, ipcSlave.Statistics.ResponsesSent);\n            Assert.AreEqual((ulong)1, ipcMaster.Statistics.ResponsesReceived);\n\n            ipcMaster.Statistics.Reset();\n\n            var empty = new RpcStatistics();\n\n            Assert.AreEqual(empty.RequestsSent, ipcMaster.Statistics.RequestsSent);\n            Assert.AreEqual(empty.ResponsesReceived, ipcMaster.Statistics.ResponsesReceived);\n            Assert.AreEqual(empty.ReadingLastMessageSize, ipcMaster.Statistics.ReadingLastMessageSize);\n            Assert.AreEqual(empty.WritingLastMessageSize, ipcMaster.Statistics.WritingLastMessageSize);\n        }\n\n        [TestMethod]\n        public void RPC_MasterCallsSlave_Exception()\n        {\n            ipcMaster = new RpcBuffer(ipcName);\n            ipcSlave = new RpcBuffer(ipcName, async (msgId, payload) =>\n            {\n                throw new Exception(\"test exception\");\n            });\n\n            var result = ipcMaster.RemoteRequest(null);\n\n            Assert.IsFalse(result.Success);\n        }\n\n        [TestMethod]\n        public void RPC_Bidirectional_Nested()\n        {\n            ipcMaster = new RpcBuffer(ipcName, async (msgId, payload) =>\n            {\n                // Ask slave to multiply the two bytes\n                return (await ipcMaster.RemoteRequestAsync(new byte[] { 3, 3 }).ConfigureAwait(false)).Data;\n            });\n            ipcSlave = new RpcBuffer(ipcName, (msgId, payload) =>\n            {\n                return new byte[] { (byte)(payload[0] * payload[1]) };\n            });\n\n            // Send request to master from slave\n            var result = ipcSlave.RemoteRequest(null);\n            Assert.IsTrue(result.Success);\n            Assert.AreEqual((3 * 3), result.Data[0]);\n        }\n\n\n        [TestMethod]\n        public void RPC_Timeout()\n        {\n            ipcMaster = new RpcBuffer(ipcName, async (msgId, payload) =>\n            {\n            }, bufferCapacity: 256);\n            ipcSlave = new RpcBuffer(ipcName, (msgId, payload) =>\n            {\n                Task.Delay(1000).Wait();\n                return new byte[] { (byte)(payload[0] * payload[1]) };\n            });\n\n            var result = ipcMaster.RemoteRequest(new byte[] { 3, 3 }, 100);\n            Assert.IsFalse(result.Success);\n\n        }\n\n        [TestMethod]\n        public void RPC_Timeout_FireAndForget()\n        {\n            ipcMaster = new RpcBuffer(ipcName, async (msgId, payload) =>\n            {\n            }, bufferCapacity: 256);\n            ipcSlave = new RpcBuffer(ipcName, (msgId, payload) =>\n            {\n                Task.Delay(1000).Wait();\n                return new byte[] { (byte)(payload[0] * payload[1]) };\n            });\n\n            var result = ipcMaster.RemoteRequest(new byte[] { 3, 3 }, 0);\n            Assert.IsFalse(result.Success);\n\n        }\n\n#if DEBUG\n        [TestMethod]\n        public void RPC_LoadTest_5k_Small()\n        {\n            ipcMaster = new RpcBuffer(ipcName, async (msgId, payload) =>\n            {\n            }, bufferCapacity: 256);\n            ipcSlave = new RpcBuffer(ipcName, (msgId, payload) =>\n            {\n                return new byte[] { (byte)(payload[0] * payload[1]) };\n            });\n\n            Stopwatch watch = Stopwatch.StartNew();\n\n            // Send request to slave from master\n            for (var i = 0; i < 5000; i++)\n            {\n                var result = ipcMaster.RemoteRequest(new byte[] { 3, 3 }, 100);\n                Assert.IsTrue(result.Success);\n                Assert.AreEqual((3 * 3), result.Data[0]);\n            }\n            watch.Stop();\n\n            Assert.IsTrue(watch.ElapsedMilliseconds < 1000);\n        }\n        \n        [TestMethod]\n        public void RPC_LoadTest_5k_Small_Multi_Thread()\n        {\n            // Warmup the Theadpool\n            ThreadPool.SetMinThreads(15, 10);\n            \n            ipcMaster = new RpcBuffer(ipcName, async (msgId, payload) =>\n            {\n            }, bufferCapacity: 256);\n            ipcSlave = new RpcBuffer(ipcName, (msgId, payload) =>\n            {\n                return new byte[] { (byte)(payload[0] * payload[1]) };\n            });\n\n            Stopwatch watch = Stopwatch.StartNew();\n\n            List<Task> tasks = new List<Task>();\n            \n            for (int i = 0; i < 10; i++)\n            {\n                tasks.Add(Task.Run(() =>\n                {\n                    // Send request to slave from master\n                    for (var j = 0; j < 5000; j++)\n                    {\n                        var result = ipcMaster.RemoteRequest(new byte[] { 3, 3 });\n                        Assert.IsTrue(result.Success);\n                        Assert.AreEqual((3 * 3), result.Data[0]);\n                    }       \n                }));\n                \n            }\n\n            Task.WaitAll(tasks.ToArray());\n            watch.Stop();\n\n            Assert.IsTrue(watch.ElapsedMilliseconds < 1000);\n        }\n\n        [TestMethod]\n        public void RPC_LoadTest_1k_Large()\n        {\n            ipcMaster = new RpcBuffer(ipcName, async (msgId, payload) =>\n            {\n            }, bufferCapacity: 1025 * 512);\n            ipcSlave = new RpcBuffer(ipcName, (msgId, payload) =>\n            {\n                return new byte[] { (byte)(payload[0] * payload[1]) };\n            });\n\n            var buf = new byte[1025 * 512];\n            buf[0] = 3;\n            buf[1] = 3;\n\n            Stopwatch watch = Stopwatch.StartNew();\n\n            // Send request to slave from master\n            for (var i = 0; i < 1000; i++)\n            {\n                var result = ipcMaster.RemoteRequest(buf, 100);\n                Assert.IsTrue(result.Success);\n                Assert.AreEqual((3 * 3), result.Data[0]);\n            }\n            watch.Stop();\n\n            Assert.IsTrue(watch.ElapsedMilliseconds < 2000);\n        }\n\n        [TestMethod]\n        public void RPC_LoadTest_NestedCalls()\n        {\n            ipcMaster = new RpcBuffer(ipcName, async (msgId, payload) =>\n            {\n                // Ask slave to multiply the two bytes\n                return (await ipcMaster.RemoteRequestAsync(new byte[] { 3, 3 }).ConfigureAwait(false)).Data;\n            });\n            ipcSlave = new RpcBuffer(ipcName, (msgId, payload) =>\n            {\n                return new byte[] { (byte)(payload[0] * payload[1]) };\n            });\n\n            Stopwatch watch = Stopwatch.StartNew();\n\n            // Send request to master from slave\n            for (var i = 0; i < 10; i++)\n            {\n                var result = ipcSlave.RemoteRequest(null, 30000);\n                Assert.IsTrue(result.Success);\n                Assert.AreEqual((3 * 3), result.Data[0]);\n            }\n            watch.Stop();\n\n            Assert.IsTrue(watch.ElapsedMilliseconds < 1000);\n        }\n#endif\n\n        [TestMethod]\n        public void RPC_SlaveCallsMasterAfterClosed_Exception()\n        {\n            ipcMaster = new RpcBuffer(ipcName, async (msgId, payload) =>\n            {\n            });\n\n            ipcSlave = new RpcBuffer(ipcName);\n\n            ipcSlave.RemoteRequest(null);\n\n            ipcMaster.Dispose();\n            while (!ipcMaster.DisposeFinished)\n            {\n                Task.Delay(125).Wait();\n            }\n\n            Assert.ThrowsException<InvalidOperationException>(() => ipcSlave.RemoteRequest(null));\n        }\n        \n        [TestMethod]\n        public void RPC_Dispose()\n        {\n            // Warmup the Theadpool\n            ThreadPool.SetMinThreads(15, 10);\n\n            for (int i = 0; i < 10; i++)\n            {\n                ipcMaster = new RpcBuffer(ipcName, async (msgId, payload) =>\n                {\n                }, bufferCapacity: 256);\n\n                ipcSlave = new RpcBuffer(ipcName, (msgId, payload) =>\n                {\n                    ipcSlave.Dispose();\n                    return new byte[] { (byte)(payload[0] * payload[1]) };\n                    \n                });\n\n                Stopwatch watch = Stopwatch.StartNew();\n\n                ipcMaster.RemoteRequestAsync(new byte[] { 3, 3 });\n                Task.Delay(125).Wait();\n                ipcSlave.Dispose();\n                watch.Stop();\n\n                ipcMaster.Dispose();\n\n                while (!ipcMaster.DisposeFinished || !ipcSlave.DisposeFinished)\n                {\n                    Task.Delay(125).Wait();\n                }\n\n                \n            }\n\n\n            \n\n         \n        }\n        \n    }\n}\n"
  },
  {
    "path": "SharedMemory.Tests/SharedMemory.Tests.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks>netcoreapp3.0;netcoreapp2.0;net47;net46;net45</TargetFrameworks>\n\n    <IsPackable>false</IsPackable>\n  </PropertyGroup>\n\n  <PropertyGroup>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"15.5.0\" />\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"15.5.0\" />\n    <PackageReference Include=\"MSTest.TestAdapter\" Version=\"1.2.0\" />\n    <PackageReference Include=\"MSTest.TestFramework\" Version=\"1.2.0\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\SharedMemory\\SharedMemory.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "SharedMemory.nuspec",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<package xmlns=\"http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd\">\n    <metadata>\n        <id>SharedMemory</id>\n        <version>$version$</version>\n        <title>SharedMemory</title>\n        <authors>Justin Stenning</authors>\n        <owners>Justin Stenning</owners>\n        <licenseUrl>https://github.com/spazzarama/SharedMemory/blob/master/LICENSE.md</licenseUrl>\n        <projectUrl>https://github.com/spazzarama/SharedMemory</projectUrl>\n        <requireLicenseAcceptance>true</requireLicenseAcceptance>\n        <description>The SharedMemory library provides a set of C# classes that utilise a memory-mapped file for fast low-level inter-process communication (IPC) - specifically for sharing data between processes.\n\nIt features:\n * a lock-free FIFO circular buffer\n * a simple fixed-size generic shared memory array class\n * a bi-directional RPC implementation (.NET 4.5+ / .NET Standard 2.0+ only)\n * an implementation of a shared memory buffer for read/write.\n * support for memory-mapped files in .NET 3.5/4/4.5 and .NET Standard 2.0+\n * fast generic structure reading/writing\n\nUsage: https://github.com/spazzarama/SharedMemory</description>\n        <summary>Shared memory classes for sharing data between processes (Array, Buffer, Circular Buffer and RPC Buffer)</summary>\n        <releaseNotes>$version$\n1. Added bi-directional RPC implementation (RpcBuffer) for .NET 4.5+ / .NET Standard 2.0+\n1. Added CopyTo/ToBytes/FromBytes/ReadBytes/WriteBytes to FastStructure\n1. Added .NET Standard 2.1 build\n</releaseNotes>\n        <copyright>Copyright (c) 2014-2020 Justin Stenning</copyright>\n        <tags>IPC RPC memory mapped file shared data circular ring buffer</tags>\n        <dependencies>\n          <group targetFramework=\".NETFramework3.5\" />\n          <group targetFramework=\".NETFramework4.0\" />\n          <group targetFramework=\".NETFramework4.5\" />\n          <group targetFramework=\".NETFramework4.6\" />\n          <group targetFramework=\".NETFramework4.7\" />\n          <group targetFramework=\".NETStandard2.0\">\n              <dependency id=\"System.Reflection.Emit.Lightweight\" version=\"4.7.0\" />\n          </group>\n          <group targetFramework=\".NETStandard2.1\">\n              <dependency id=\"System.Reflection.Emit.Lightweight\" version=\"4.7.0\" />\n          </group>\n        </dependencies>     \n    </metadata>\n    <files>\n        <file src=\".\\bin\\Release\\Net47\\SharedMemory.dll\" target=\"lib\\net47\" />\n        <file src=\".\\bin\\Release\\Net47\\SharedMemory.pdb\" target=\"lib\\net47\" />\n        <file src=\".\\bin\\Release\\Net47\\SharedMemory.xml\" target=\"lib\\net47\" />\n        <file src=\".\\bin\\Release\\Net46\\SharedMemory.dll\" target=\"lib\\net46\" />\n        <file src=\".\\bin\\Release\\Net46\\SharedMemory.pdb\" target=\"lib\\net46\" />\n        <file src=\".\\bin\\Release\\Net46\\SharedMemory.xml\" target=\"lib\\net46\" />\t\n        <file src=\".\\bin\\Release\\Net45\\SharedMemory.dll\" target=\"lib\\net45\" />\n        <file src=\".\\bin\\Release\\Net45\\SharedMemory.pdb\" target=\"lib\\net45\" />\n        <file src=\".\\bin\\Release\\Net45\\SharedMemory.xml\" target=\"lib\\net45\" />\n        <file src=\".\\bin\\Release\\Net4\\SharedMemory.dll\" target=\"lib\\net40\" />\n        <file src=\".\\bin\\Release\\Net4\\SharedMemory.pdb\" target=\"lib\\net40\" />\n        <file src=\".\\bin\\Release\\Net4\\SharedMemory.xml\" target=\"lib\\net40\" />\n        <file src=\".\\bin\\Release\\Net35\\SharedMemory.dll\" target=\"lib\\net35\" />\n        <file src=\".\\bin\\Release\\Net35\\SharedMemory.pdb\" target=\"lib\\net35\" />\n        <file src=\".\\bin\\Release\\Net35\\SharedMemory.xml\" target=\"lib\\net35\" />\n        <file src=\".\\bin\\Release\\netstandard2.0\\SharedMemory.dll\" target=\"lib\\netstandard2.0\" />\n        <file src=\".\\bin\\Release\\netstandard2.0\\SharedMemory.pdb\" target=\"lib\\netstandard2.0\" />\n        <file src=\".\\bin\\Release\\netstandard2.0\\SharedMemory.xml\" target=\"lib\\netstandard2.0\" />\n        <file src=\".\\bin\\Release\\netstandard2.1\\SharedMemory.dll\" target=\"lib\\netstandard2.1\" />\n        <file src=\".\\bin\\Release\\netstandard2.1\\SharedMemory.pdb\" target=\"lib\\netstandard2.1\" />\n        <file src=\".\\bin\\Release\\netstandard2.1\\SharedMemory.xml\" target=\"lib\\netstandard2.1\" />\n        <file src=\".\\SharedMemory\\**\\*.cs\" exclude=\".\\SharedMemory\\obj\\**\\*.*\" target=\"src\" />\n    </files>\n</package>"
  },
  {
    "path": "SharedMemory.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.29728.190\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"SharedMemory\", \"SharedMemory\\SharedMemory.csproj\", \"{49EEAE24-99C6-48F9-8784-4C9196BD1958}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"SharedMemory.Tests\", \"SharedMemory.Tests\\SharedMemory.Tests.csproj\", \"{51DD9C46-955A-454A-952E-44046FFBEEB5}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Examples\", \"Examples\", \"{D3675823-5797-428B-A5D4-83824612F924}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"ClientTest\", \"Examples\\ClientTest\\ClientTest.csproj\", \"{69FC3A5E-C4C7-47BA-B553-FF9477D56B9B}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"ServerTest\", \"Examples\\ServerTest\\ServerTest.csproj\", \"{6FC3F56C-B2DD-4E81-8425-CCAF2A6FF343}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"SingleProcess\", \"Examples\\SingleProcess\\SingleProcess.csproj\", \"{58858E6C-4455-443F-A288-2B4BDCEDFB10}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"RpcTest\", \"Examples\\RpcTest\\RpcTest.csproj\", \"{295402B7-EEF8-4A79-968E-58135AE5D385}\"\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{49EEAE24-99C6-48F9-8784-4C9196BD1958}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{49EEAE24-99C6-48F9-8784-4C9196BD1958}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{49EEAE24-99C6-48F9-8784-4C9196BD1958}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{49EEAE24-99C6-48F9-8784-4C9196BD1958}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{51DD9C46-955A-454A-952E-44046FFBEEB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{51DD9C46-955A-454A-952E-44046FFBEEB5}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{51DD9C46-955A-454A-952E-44046FFBEEB5}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{51DD9C46-955A-454A-952E-44046FFBEEB5}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{69FC3A5E-C4C7-47BA-B553-FF9477D56B9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{69FC3A5E-C4C7-47BA-B553-FF9477D56B9B}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{69FC3A5E-C4C7-47BA-B553-FF9477D56B9B}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{69FC3A5E-C4C7-47BA-B553-FF9477D56B9B}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{6FC3F56C-B2DD-4E81-8425-CCAF2A6FF343}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{6FC3F56C-B2DD-4E81-8425-CCAF2A6FF343}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{6FC3F56C-B2DD-4E81-8425-CCAF2A6FF343}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{6FC3F56C-B2DD-4E81-8425-CCAF2A6FF343}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{58858E6C-4455-443F-A288-2B4BDCEDFB10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{58858E6C-4455-443F-A288-2B4BDCEDFB10}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{58858E6C-4455-443F-A288-2B4BDCEDFB10}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{58858E6C-4455-443F-A288-2B4BDCEDFB10}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{295402B7-EEF8-4A79-968E-58135AE5D385}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{295402B7-EEF8-4A79-968E-58135AE5D385}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{295402B7-EEF8-4A79-968E-58135AE5D385}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{295402B7-EEF8-4A79-968E-58135AE5D385}.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{69FC3A5E-C4C7-47BA-B553-FF9477D56B9B} = {D3675823-5797-428B-A5D4-83824612F924}\n\t\t{6FC3F56C-B2DD-4E81-8425-CCAF2A6FF343} = {D3675823-5797-428B-A5D4-83824612F924}\n\t\t{58858E6C-4455-443F-A288-2B4BDCEDFB10} = {D3675823-5797-428B-A5D4-83824612F924}\n\t\t{295402B7-EEF8-4A79-968E-58135AE5D385} = {D3675823-5797-428B-A5D4-83824612F924}\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {DD0E1614-8CF6-460D-95BD-8E178CA17132}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "appveyor-develop.yml",
    "content": "version: 2.2.{build}\npull_requests:\n  do_not_increment_build_number: true\nbranches:\n  only:\n  - master\nskip_tags: true\nimage: Visual Studio 2019\nconfiguration: Release\nplatform: Any CPU\nshallow_clone: true\nbuild_script:\n- cmd: >-\n    dotnet build -f netstandard2.1 -v m /p:Version=%APPVEYOR_BUILD_VERSION% .\\SharedMemory\\SharedMemory.csproj\n\n    dotnet build -f netstandard2.0 -v m /p:Version=%APPVEYOR_BUILD_VERSION% .\\SharedMemory\\SharedMemory.csproj\n\n    dotnet build -f netcoreapp3.0 -v m /p:Version=%APPVEYOR_BUILD_VERSION% .\\SharedMemory.Tests\\SharedMemory.Tests.csproj\n\n    dotnet build -f netcoreapp2.0 -v m /p:Version=%APPVEYOR_BUILD_VERSION% .\\SharedMemory.Tests\\SharedMemory.Tests.csproj\n\n    dotnet build -f net47 -v m /p:Version=%APPVEYOR_BUILD_VERSION%\n\n    dotnet build -f net46 -v m /p:Version=%APPVEYOR_BUILD_VERSION%\n\n    dotnet build -f net45 -v m /p:Version=%APPVEYOR_BUILD_VERSION%\n\n    msbuild .\\SharedMemory\\SharedMemory.csproj /verbosity:m /p:TargetFramework=net4 /p:Version=%APPVEYOR_BUILD_VERSION%\n\n    msbuild .\\SharedMemory\\SharedMemory.csproj /verbosity:m /p:TargetFramework=net35 /p:Version=%APPVEYOR_BUILD_VERSION%\n\n    nuget pack SharedMemory.nuspec -Symbols -Version %APPVEYOR_BUILD_VERSION%\nartifacts:\n- path: bin\\Release\\net47\\SharedMemory.dll\n  name: SharedMemory.dll\n- path: Examples\\SingleProcess\\bin\\Release\\net47\\SingleProcess.exe\n  name: SingleProcess.exe\n- path: Examples\\ClientTest\\bin\\Release\\net47\\ClientTest.exe\n  name: ClientTest.exe\n- path: Examples\\ServerTest\\bin\\Release\\net47\\ServerTest.exe\n  name: ServerTest.exe\n- path: Examples\\RpcTest\\bin\\Release\\net47\\RpcTest.exe\n  name: RpcTest.exe\n- path: '**\\*.nupkg'\n  name: NuGet"
  },
  {
    "path": "appveyor.yml",
    "content": "version: 2.3.{build}\npull_requests:\n  do_not_increment_build_number: true\nbranches:\n  only:\n  - master\nskip_tags: true\nimage: Visual Studio 2019\nconfiguration: Release\nplatform: Any CPU\nshallow_clone: true\nbuild_script:\n- cmd: >-\n    dotnet build -f netstandard2.1 -v m /p:Version=%APPVEYOR_BUILD_VERSION% .\\SharedMemory\\SharedMemory.csproj\n\n    dotnet build -f netstandard2.0 -v m /p:Version=%APPVEYOR_BUILD_VERSION% .\\SharedMemory\\SharedMemory.csproj\n\n    dotnet build -f netcoreapp3.0 -v m /p:Version=%APPVEYOR_BUILD_VERSION% .\\SharedMemory.Tests\\SharedMemory.Tests.csproj\n\n    dotnet build -f netcoreapp2.0 -v m /p:Version=%APPVEYOR_BUILD_VERSION% .\\SharedMemory.Tests\\SharedMemory.Tests.csproj\n\n    dotnet build -f net47 -v m /p:Version=%APPVEYOR_BUILD_VERSION%\n\n    dotnet build -f net46 -v m /p:Version=%APPVEYOR_BUILD_VERSION%\n\n    dotnet build -f net45 -v m /p:Version=%APPVEYOR_BUILD_VERSION%\n\n    msbuild .\\SharedMemory\\SharedMemory.csproj /verbosity:m /p:TargetFramework=net4 /p:Version=%APPVEYOR_BUILD_VERSION%\n\n    msbuild .\\SharedMemory\\SharedMemory.csproj /verbosity:m /p:TargetFramework=net35 /p:Version=%APPVEYOR_BUILD_VERSION%\n\n    nuget pack SharedMemory.nuspec -Symbols -Version %APPVEYOR_BUILD_VERSION%\nartifacts:\n- path: bin\\Release\\net47\\SharedMemory.dll\n  name: SharedMemory.dll\n- path: Examples\\SingleProcess\\bin\\Release\\net47\\SingleProcess.exe\n  name: SingleProcess.exe\n- path: Examples\\ClientTest\\bin\\Release\\net47\\ClientTest.exe\n  name: ClientTest.exe\n- path: Examples\\ServerTest\\bin\\Release\\net47\\ServerTest.exe\n  name: ServerTest.exe\n- path: Examples\\RpcTest\\bin\\Release\\net47\\RpcTest.exe\n  name: RpcTest.exe\n- path: '**\\*.nupkg'\n  name: NuGet\ndeploy:\n- provider: NuGet\n  api_key:\n    secure: e4koh5XAZTPiyEU1naOGPiO3wvz14pD6huFncm2ffzxbO+Vh0kNX2lOBQoa/BV+2\n"
  }
]