Full Code of justinstenning/SharedMemory for AI

main d80348e07a2e cached
44 files
407.7 KB
86.7k tokens
396 symbols
1 requests
Download .txt
Showing preview only (426K chars total). Download the full file or copy to clipboard to get everything.
Repository: justinstenning/SharedMemory
Branch: main
Commit: d80348e07a2e
Files: 44
Total size: 407.7 KB

Directory structure:
gitextract_lcci0mpz/

├── .gitignore
├── Examples/
│   ├── ClientTest/
│   │   ├── ClientTest.csproj
│   │   └── Program.cs
│   ├── RpcTest/
│   │   ├── Program.cs
│   │   └── RpcTest.csproj
│   ├── ServerTest/
│   │   ├── Program.cs
│   │   └── ServerTest.csproj
│   └── SingleProcess/
│       ├── CommandLineParser.cs
│       ├── Program.cs
│       └── SingleProcess.csproj
├── LICENSE.md
├── README.md
├── SharedMemory/
│   ├── BufferReadWrite.cs
│   ├── BufferWithLocks.cs
│   ├── CircularBuffer.cs
│   ├── FastStructure.cs
│   ├── MemoryMappedFiles/
│   │   ├── MemoryMappedFile.cs
│   │   ├── MemoryMappedFileAccess.cs
│   │   ├── MemoryMappedFileRights.cs
│   │   ├── MemoryMappedView.cs
│   │   ├── MemoryMappedViewAccessor.cs
│   │   ├── SafeMemoryMappedFileHandle.cs
│   │   └── SafeMemoryMappedViewHandle.cs
│   ├── RpcBuffer.cs
│   ├── SharedArray.cs
│   ├── SharedBuffer.cs
│   ├── SharedHeader.cs
│   ├── SharedMemory.csproj
│   ├── SharedMemory.licenseheader
│   ├── UnsafeNativeMethods.cs
│   └── Utilities/
│       ├── ArraySlice.cs
│       └── ExpandingArray.cs
├── SharedMemory.Tests/
│   ├── ArraySliceTests.cs
│   ├── ArrayTests.cs
│   ├── BufferReadWriteTests.cs
│   ├── CircularBufferTests.cs
│   ├── ExpandingArrayTests.cs
│   ├── FastStructureTests.cs
│   ├── RpcBufferTests.cs
│   └── SharedMemory.Tests.csproj
├── SharedMemory.nuspec
├── SharedMemory.sln
├── appveyor-develop.yml
└── appveyor.yml

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
#ignore thumbnails created by windows
Thumbs.db
#Ignore files build by Visual Studio
*.user
*.aps
*.pch
*.vspscc
*_i.c
*_p.c
*.ncb
*.suo
*.bak
*.cache
*.ilk
*.log
[Bb]in
[Dd]ebug*/
*.sbr
obj/
[Rr]elease*/
_ReSharper*/
TestResults*/
*.nupkg
/.vs/SharedMemory
/nuget.exe
/.vs/ProjectSettings.json
/.vs/slnx.sqlite
/.vs/VSWorkspaceState.json

# JetBrains Rider
.idea/
*.sln.iml


================================================
FILE: Examples/ClientTest/ClientTest.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>netcoreapp3.0;netcoreapp2.0;net47;net46;net45;net4;net35</TargetFrameworks>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\SharedMemory\SharedMemory.csproj" />
  </ItemGroup>

</Project>


================================================
FILE: Examples/ClientTest/Program.cs
================================================
// SharedMemory (File: ClientTest\Program.cs)
// Copyright (c) 2014 Justin Stenning
// http://spazzarama.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// The SharedMemory library is inspired by the following Code Project article:
//   "Fast IPC Communication Using Shared Memory and InterlockedCompareExchange"
//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using SharedMemory;

#if NET40Plus
using System.Threading.Tasks;
#endif

namespace ClientTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Press <enter> to start client");

            Console.ReadLine();

            Console.WriteLine("Open existing shared memory circular buffer");
            using (SharedMemory.CircularBuffer theClient = new SharedMemory.CircularBuffer("TEST"))
            {
                Console.WriteLine("Buffer {0} opened, NodeBufferSize: {1}, NodeCount: {2}", theClient.Name, theClient.NodeBufferSize, theClient.NodeCount);

                long bufferSize = theClient.NodeBufferSize;
                byte[] writeDataProof;
                byte[] writeData = new byte[bufferSize];

                List<byte[]> dataList = new List<byte[]>();

                // Generate data for integrity check
                for (var j = 0; j < 256; j++)
                {
                    var data = new byte[bufferSize];
                    for (var i = 0; i < data.Length; i++)
                    {
                        data[i] = (byte)((i + j) % 255);
                    }
                    dataList.Add(data);
                }

                int skipCount = 0;
                long iterations = 0;
                long totalBytes = 0;
                long lastTick = 0;
                Stopwatch sw = Stopwatch.StartNew();

                int threadCount = 0;
                Action reader = () =>
                {
                    int myThreadIndex = Interlocked.Increment(ref threadCount);
                    int linesOut = 0;
                    bool finalLine = false;
                    for (; ; )
                    {
                        int amount = theClient.Read(writeData, 100);
                        //int amount = theClient.Read<byte>(writeData, 100);

                        if (amount == 0)
                        {
                            Interlocked.Increment(ref skipCount);
                        }
                        else
                        {
                            // Only check data integrity for first thread
                            if (threadCount == 1)
                            {
                                bool mismatch = false;

                                writeDataProof = dataList[((int)Interlocked.Read(ref iterations)) % 255];
                                for (var i = 0; i < writeDataProof.Length; i++)
                                {
                                    if (writeData[i] != writeDataProof[i])
                                    {
                                        mismatch = true;
                                        Console.WriteLine("Buffers don't match!");
                                        break;
                                    }
                                }

                                if (mismatch)
                                    break;
                            }

                            Interlocked.Add(ref totalBytes, amount);

                            Interlocked.Increment(ref iterations);
                        }

                        if (threadCount == 1 && Interlocked.Read(ref iterations) > 500)
                            finalLine = true;

                        if (myThreadIndex < 3 && (finalLine || sw.ElapsedTicks - lastTick > 1000000))
                        {
                            lastTick = sw.ElapsedTicks;
                            Console.WriteLine("Read: {0}, Wait: {1}, {2}MB/s", ((double)totalBytes / 1048576.0).ToString("F0"), skipCount, (((totalBytes / 1048576.0) / sw.ElapsedMilliseconds) * 1000).ToString("F2"));
                            linesOut++;
                            if (finalLine || (myThreadIndex > 1 && linesOut > 10))
                            {
                                Console.WriteLine("Completed.");
                                break;
                            }
                        }
                    }
                };

                Console.WriteLine("Testing data integrity (high CPU, low bandwidth)...");
                reader();
                Console.WriteLine("");

                skipCount = 0;
                iterations = 0;
                totalBytes = 0;
                lastTick = 0;
                sw.Reset();
                sw.Start();
                Console.WriteLine("Testing data throughput (low CPU, high bandwidth)...");
#if NET40Plus                
                Task c1 = Task.Factory.StartNew(reader);
                Task c2 = Task.Factory.StartNew(reader);
                Task c3 = Task.Factory.StartNew(reader);
                //Task c4 = Task.Factory.StartNew(reader);
#else
                ThreadPool.QueueUserWorkItem((o) => { reader(); });
                ThreadPool.QueueUserWorkItem((o) => { reader(); });
                ThreadPool.QueueUserWorkItem((o) => { reader(); });
#endif
                Console.ReadLine();
            }
        }
    }
}


================================================
FILE: Examples/RpcTest/Program.cs
================================================
// SharedMemory (File: RpcTest\Program.cs)
// Copyright (c) 2020 Justin Stenning
// http://spazzarama.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using SharedMemory;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace RpcTest
{
    class Program
    {
        static void Main(string[] args)
        {
            long completed = 0;
            long count = 0;
            byte[][] dataList;
            int loopCount = 2000;
            int bufSize = 1024 * 500;
            int bufferCapacity = bufSize + 64; // buf size + enough room for protocol header
            int threadCount = 1;
            int dataListCount = 256;
            
            // Generate random data to be written
            Random random = new Random();
            dataList = new byte[dataListCount][];
            for (var j = 0; j < dataListCount; j++)
            {
                var data = new byte[bufSize];
                random.NextBytes(data);
                dataList[j] = data;
            }

            Console.WriteLine($"Thread count: {threadCount}");
            Console.WriteLine($"Buffer size: {bufferCapacity}");
            Console.WriteLine($"Message size: {bufSize}");

            Console.WriteLine("Running...");

            Stopwatch watch = Stopwatch.StartNew();

            for (var i = 0; i < threadCount; i++)
            {
                new Task(async () =>
                    {
                        RpcBuffer ipcMaster = null;
                        RpcBuffer ipcSlave = null;
                        var name = $"MasterSlaveTest{Guid.NewGuid()}";
                        ipcMaster = new RpcBuffer(name, bufferCapacity: bufferCapacity);
                        ipcSlave = new RpcBuffer(name, (msgId, payload) =>
                        {
                            Interlocked.Increment(ref count);
                            return (byte[])null;
                            //return new byte[] { (byte)(payload[0] * payload[1]) };
                        });
                        var rnd = new Random();
                        var watchLine = Stopwatch.StartNew();
                        for (var j = 0; j < loopCount; j++)
                        {
                            var result = await ipcMaster.RemoteRequestAsync(dataList[rnd.Next(0, dataList.Length)]);
                            if (!result.Success)
                            {
                                Console.WriteLine("Failed");
                                return;
                            }
                        }
                        Interlocked.Increment(ref completed);
                    }).Start();
            }

            while(Interlocked.Read(ref completed) < threadCount)
            {
                Thread.Sleep(0);
            }

            watch.Stop();
            Console.WriteLine($"{count} in {watch.Elapsed}, {(int)(count / watch.Elapsed.TotalSeconds)} requests / sec");

            Console.ReadLine();
        }
    }
}


================================================
FILE: Examples/RpcTest/RpcTest.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>netcoreapp3.0;netcoreapp2.0;net47;net46;net45</TargetFrameworks>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\SharedMemory\SharedMemory.csproj" />
  </ItemGroup>

  <ItemGroup>
    <Folder Include="Properties\" />
  </ItemGroup>
</Project>


================================================
FILE: Examples/ServerTest/Program.cs
================================================
// SharedMemory (File: ServerTest\Program.cs)
// Copyright (c) 2014 Justin Stenning
// http://spazzarama.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// The SharedMemory library is inspired by the following Code Project article:
//   "Fast IPC Communication Using Shared Memory and InterlockedCompareExchange"
//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
#if NET40Plus
using System.Threading.Tasks;
#endif

namespace ServerTest
{
    class Program
    {
        static void Main(string[] args)
        {
            int bufferSize = 1048576;
            byte[] readData;
            int size = sizeof(byte) * bufferSize;
            int count = 50;

            // Generate data to be written
            byte[][] dataList = new byte[256][];
            for (var j = 0; j < 256; j++)
            {
                var data = new byte[bufferSize];
                for (var i = 0; i < data.Length; i++)
                {
                    data[i] = (byte)((i + j) % 255);
                }
                dataList[j] = data;
            }

            Console.WriteLine("Press <enter> to start Server");
            Console.ReadLine();


            Console.WriteLine("Create shared memory circular buffer");
            using (var theServer = new SharedMemory.CircularBuffer("TEST", count, size))
            {
                Console.WriteLine("Circular buffer producer created.");
                Console.WriteLine("Ready for client...");
                Thread.Sleep(1000);

                int skipCount = 0;
                long iterations = 0;
                long totalBytes = 0;
                long lastTick = 0;
                Stopwatch sw = Stopwatch.StartNew();                
                int threadCount = 0;
                Action writer = () =>
                {
                    int myThreadIndex = Interlocked.Increment(ref threadCount);
                    int linesOut = 0;
                    bool finalLine = false;
                    for (; ; )
                    {
                        readData = dataList[iterations % 255];

                        int amount = theServer.Write(readData, 100);
                        //int amount = theServer.Write<byte>(readData, 100);

                        if (amount == 0)
                        {
                            Interlocked.Increment(ref skipCount);
                        }
                        else
                        {
                            Interlocked.Add(ref totalBytes, amount);
                            Interlocked.Increment(ref iterations);
                        }

                        if (threadCount == 1 && Interlocked.Read(ref iterations) > 500)
                            finalLine = true;

                        if (myThreadIndex < 3 && (finalLine || sw.ElapsedTicks - lastTick > 1000000))
                        {
                            lastTick = sw.ElapsedTicks;
                            Console.WriteLine("Write: {0}, Wait: {1}, {2}MB/s", ((double)totalBytes / 1048576.0).ToString("F0"), skipCount, (((totalBytes / 1048576.0) / sw.ElapsedMilliseconds) * 1000).ToString("F2"));
                            linesOut++;
                            if (finalLine || (myThreadIndex > 1 && linesOut > 10))
                            {
                                Console.WriteLine("Completed.");
                                break;
                            }
                        }
                    }
                };

                writer();
                Console.WriteLine("");
                skipCount = 0;
                iterations = 0;
                totalBytes = 0;
                lastTick = 0;
                sw.Reset();
                sw.Start();

                Console.WriteLine("Testing throughput...");
#if NET40Plus
                Task s1 = Task.Factory.StartNew(writer);
                //Task s2 = Task.Factory.StartNew(writer);
                //Task s3 = Task.Factory.StartNew(writer);
#else
                ThreadPool.QueueUserWorkItem((o) =>
                {
                    writer();
                });
#endif
                Console.ReadLine();
            }
        }
    }
}


================================================
FILE: Examples/ServerTest/ServerTest.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>netcoreapp3.0;netcoreapp2.0;net47;net46;net45;net4;net35</TargetFrameworks>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\SharedMemory\SharedMemory.csproj" />
  </ItemGroup>

</Project>


================================================
FILE: Examples/SingleProcess/CommandLineParser.cs
================================================
//////////////////////////////////////////////////////////////////////////////
//    Command Line Argument Parser
//    ----------------------------
//
//    Author: hotweird@hotmail.com
//
//    Microsoft Public License (Ms-PL)
//
//    This license governs use of the accompanying software. If you use the software, you
//    accept this license. If you do not accept the license, do not use the software.
//
//    1. Definitions
//
//    The terms "reproduce," "reproduction," "derivative works," and "distribution" have the
//    same meaning here as under U.S. copyright law.
//
//    A "contribution" is the original software, or any additions or changes to the software.
//
//    A "contributor" is any person that distributes its contribution under this license.
//
//    "Licensed patents" are a contributor's patent claims that read directly on its contribution.
//
//    2. Grant of Rights
//
//    (A) Copyright Grant- Subject to the terms of this license, including the license conditions
//        and limitations in section 3, each contributor grants you a non-exclusive, worldwide,
//        royalty-free copyright license to reproduce its contribution, prepare derivative works
//        of its contribution, and distribute its contribution or any derivative works that you create.
//
//    (B) Patent Grant- Subject to the terms of this license, including the license conditions and
//        limitations in section 3, each contributor grants you a non-exclusive, worldwide,
//        royalty-free license under its licensed patents to make, have made, use, sell, offer for
//        sale, import, and/or otherwise dispose of its contribution in the software or derivative
//        works of the contribution in the software.
//
//    3. Conditions and Limitations
//
//    (A) No Trademark License- This license does not grant you rights to use any contributors'
//        name, logo, or trademarks.
//
//    (B) If you bring a patent claim against any contributor over patents that you claim are
//        infringed by the software, your patent license from such contributor to the software ends
//        automatically.
//
//    (C) If you distribute any portion of the software, you must retain all copyright, patent,
//        trademark, and attribution notices that are present in the software.
//
//    (D) If you distribute any portion of the software in source code form, you may do so only under
//        this license by including a complete copy of this license with your distribution. If you
//        distribute any portion of the software in compiled or object code form, you may only do so
//        under a license that complies with this license.
//
//    (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no
//        express warranties, guarantees or conditions. You may have additional consumer rights under
//        your local laws which this license cannot change. To the extent permitted under your local
//        laws, the contributors exclude the implied warranties of merchantability, fitness for a
//        particular purpose and non-infringement.
//
//    Usage
//    -----
//
//    Parsing command line arguments to a console application is a common problem. 
//    This library handles the common task of reading arguments from a command line 
//    and filling in the values in a type.
//
//    To use this library, define a class whose fields represent the data that your 
//    application wants to receive from arguments on the command line. Then call 
//    CommandLine.ParseArguments() to fill the object with the data 
//    from the command line. Each field in the class defines a command line argument. 
//    The type of the field is used to validate the data read from the command line. 
//    The name of the field defines the name of the command line option.
//
//    The parser can handle fields of the following types:
//
//    - string
//    - int
//    - uint
//    - bool
//    - enum
//    - array of the above type
//
//    For example, suppose you want to read in the argument list for wc (word count). 
//    wc takes three optional boolean arguments: -l, -w, and -c and a list of files.
//
//    You could parse these arguments using the following code:
//
//    class WCArguments
//    {
//        public bool lines;
//        public bool words;
//        public bool chars;
//        public string[] files;
//    }
//
//    class WC
//    {
//        static void Main(string[] args)
//        {
//            if (CommandLine.ParseArgumentsWithUsage(args, parsedArgs))
//            {
//            //     insert application code here
//            }
//        }
//    }
//
//    So you could call this aplication with the following command line to count 
//    lines in the foo and bar files:
//
//        wc.exe /lines /files:foo /files:bar
//
//    The program will display the following usage message when bad command line 
//    arguments are used:
//
//        wc.exe -x
//
//    Unrecognized command line argument '-x'
//        /lines[+|-]                         short form /l
//        /words[+|-]                         short form /w
//        /chars[+|-]                         short form /c
//        /files:<string>                     short form /f
//        @<file>                             Read response file for more options
//
//    That was pretty easy. However, you realy want to omit the "/files:" for the 
//    list of files. The details of field parsing can be controled using custom 
//    attributes. The attributes which control parsing behaviour are:
//
//    ArgumentAttribute 
//        - controls short name, long name, required, allow duplicates, default value
//        and help text
//    DefaultArgumentAttribute 
//        - allows omition of the "/name".
//        - This attribute is allowed on only one field in the argument class.
//
//    So for the wc.exe program we want this:
//
//    using System;
//    using Utilities;
//
//    class WCArguments
//    {
//        [Argument(ArgumentType.AtMostOnce, HelpText="Count number of lines in the input text.")]
//        public bool lines;
//        [Argument(ArgumentType.AtMostOnce, HelpText="Count number of words in the input text.")]
//        public bool words;
//        [Argument(ArgumentType.AtMostOnce, HelpText="Count number of chars in the input text.")]
//        public bool chars;
//        [DefaultArgument(ArgumentType.MultipleUnique, HelpText="Input files to count.")]
//        public string[] files;
//    }
//
//    class WC
//    {
//        static void Main(string[] args)
//        {
//            WCArguments parsedArgs = new WCArguments();
//            if (CommandLine.ParseArgumentsWithUsage(args, parsedArgs))
//            {
//            //     insert application code here
//            }
//        }
//    }
//
//
//
//    So now we have the command line we want:
//
//        wc.exe /lines foo bar
//
//    This will set lines to true and will set files to an array containing the 
//    strings "foo" and "bar".
//
//    The new usage message becomes:
//
//        wc.exe -x
//
//    Unrecognized command line argument '-x'
//    /lines[+|-]  Count number of lines in the input text. (short form /l)
//    /words[+|-]  Count number of words in the input text. (short form /w)
//    /chars[+|-]  Count number of chars in the input text. (short form /c)
//    @<file>      Read response file for more options
//    <files>      Input files to count. (short form /f)
//
//    If you want more control over how error messages are reported, how /help is 
//    dealt with, etc you can instantiate the CommandLine.Parser class.
//
//
//
//    Cheers,
//    Peter Hallam
//    C# Compiler Developer
//    Microsoft Corp.
//
//
//
//
//    Release Notes
//    -------------
//
//    10/02/2002 Initial Release
//    10/14/2002 Bug Fix
//    01/08/2003 Bug Fix in @ include files
//    10/23/2004 Added user specified help text, formatting of help text to 
//            screen width. Added ParseHelp for /?.
//    11/23/2004 Added support for default values.
//    02/23/2005 Fix bug with short name and default arguments.
//////////////////////////////////////////////////////////////////////////////
namespace CommandLine
{
    using System;
    using System.Diagnostics;
    using System.Reflection;
    using System.Collections;
    using System.IO;
    using System.Text;
    using System.Runtime.InteropServices;

    /// <summary>
    /// Used to control parsing of command line arguments.
    /// </summary>
    [Flags]    
    public enum ArgumentType
    {
        /// <summary>
        /// Indicates that this field is required. An error will be displayed
        /// if it is not present when parsing arguments.
        /// </summary>
        Required    = 0x01,
        /// <summary>
        /// Only valid in conjunction with Multiple.
        /// Duplicate values will result in an error.
        /// </summary>
        Unique      = 0x02,
        /// <summary>
        /// Inidicates that the argument may be specified more than once.
        /// Only valid if the argument is a collection
        /// </summary>
        Multiple    = 0x04,

        /// <summary>
        /// The default type for non-collection arguments.
        /// The argument is not required, but an error will be reported if it is specified more than once.
        /// </summary>
        AtMostOnce  = 0x00,
        
        /// <summary>
        /// For non-collection arguments, when the argument is specified more than
        /// once no error is reported and the value of the argument is the last
        /// value which occurs in the argument list.
        /// </summary>
        LastOccurenceWins = Multiple,

        /// <summary>
        /// The default type for collection arguments.
        /// The argument is permitted to occur multiple times, but duplicate 
        /// values will cause an error to be reported.
        /// </summary>
        MultipleUnique  = Multiple | Unique,
    }
    
    /// <summary>
    /// Allows control of command line parsing.
    /// Attach this attribute to instance fields of types used
    /// as the destination of command line argument parsing.
    /// </summary>
    [AttributeUsage(AttributeTargets.Field)]
    public class ArgumentAttribute : Attribute
    {
        /// <summary>
        /// Allows control of command line parsing.
        /// </summary>
        /// <param name="type"> Specifies the error checking to be done on the argument. </param>
        public ArgumentAttribute(ArgumentType type)
        {
            this.type = type;
        }
        
        /// <summary>
        /// The error checking to be done on the argument.
        /// </summary>
        public ArgumentType Type
        {
            get { return this.type; }
        }
        /// <summary>
        /// Returns true if the argument did not have an explicit short name specified.
        /// </summary>
        public bool DefaultShortName    { get { return null == this.shortName; } }
        
        /// <summary>
        /// The short name of the argument.
        /// Set to null means use the default short name if it does not
        /// conflict with any other parameter name.
        /// Set to String.Empty for no short name.
        /// This property should not be set for DefaultArgumentAttributes.
        /// </summary>
        public string ShortName
        {
            get { return this.shortName; }
            set { Debug.Assert(value == null || !(this is DefaultArgumentAttribute)); this.shortName = value; }
        }

        /// <summary>
        /// Returns true if the argument did not have an explicit long name specified.
        /// </summary>
        public bool DefaultLongName     { get { return null == this.longName; } }
        
        /// <summary>
        /// The long name of the argument.
        /// Set to null means use the default long name.
        /// The long name for every argument must be unique.
        /// It is an error to specify a long name of String.Empty.
        /// </summary>
        public string LongName
        {
            get { Debug.Assert(!this.DefaultLongName); return this.longName; }
            set { Debug.Assert(value != ""); this.longName = value; }
        }

        /// <summary>
        /// The default value of the argument.
        /// </summary>
        public object DefaultValue
        {
            get { return this.defaultValue; }
            set { this.defaultValue = value; }
        }
        
        /// <summary>
        /// Returns true if the argument has a default value.
        /// </summary>
        public bool HasDefaultValue     { get { return null != this.defaultValue; } }

        /// <summary>
        /// Returns true if the argument has help text specified.
        /// </summary>
        public bool HasHelpText         { get { return null != this.helpText; } }
        
        /// <summary>
        /// The help text for the argument.
        /// </summary>
        public string HelpText
        {
            get { return this.helpText; }
            set { this.helpText = value; }
        }
        
        private string shortName;
        private string longName;
        private string helpText;
        private object defaultValue;
        private ArgumentType type;
    }

    /// <summary>
    /// Indicates that this argument is the default argument.
    /// '/' or '-' prefix only the argument value is specified.
    /// The ShortName property should not be set for DefaultArgumentAttribute
    /// instances. The LongName property is used for usage text only and
    /// does not affect the usage of the argument.
    /// </summary>
    [AttributeUsage(AttributeTargets.Field)]
    public class DefaultArgumentAttribute : ArgumentAttribute
    {
        /// <summary>
        /// Indicates that this argument is the default argument.
        /// </summary>
        /// <param name="type"> Specifies the error checking to be done on the argument. </param>
        public DefaultArgumentAttribute(ArgumentType type)
            : base (type)
        {
        }
    }

    /// <summary>
    /// A delegate used in error reporting.
    /// </summary>
    public delegate void ErrorReporter(string message);

    /// <summary>
    /// Parser for command line arguments.
    ///
    /// The parser specification is infered from the instance fields of the object
    /// specified as the destination of the parse.
    /// Valid argument types are: int, uint, string, bool, enums
    /// Also argument types of Array of the above types are also valid.
    /// 
    /// Error checking options can be controlled by adding a ArgumentAttribute
    /// to the instance fields of the destination object.
    ///
    /// At most one field may be marked with the DefaultArgumentAttribute
    /// indicating that arguments without a '-' or '/' prefix will be parsed as that argument.
    ///
    /// If not specified then the parser will infer default options for parsing each
    /// instance field. The default long name of the argument is the field name. The
    /// default short name is the first character of the long name. Long names and explicitly
    /// specified short names must be unique. Default short names will be used provided that
    /// the default short name does not conflict with a long name or an explicitly
    /// specified short name.
    ///
    /// Arguments which are array types are collection arguments. Collection
    /// arguments can be specified multiple times.
    /// </summary>
    public sealed class Parser
    {
        /// <summary>
        /// The System Defined new line string.
        /// </summary>
        public const string NewLine = "\r\n";
        
        /// <summary>
        /// Don't ever call this.
        /// </summary>
        private Parser() { }
        
        /// <summary>
        /// Parses Command Line Arguments. Displays usage message to Console.Out
        /// if /?, /help or invalid arguments are encounterd.
        /// Errors are output on Console.Error.
        /// Use ArgumentAttributes to control parsing behaviour.
        /// </summary>
        /// <param name="arguments"> The actual arguments. </param>
        /// <param name="destination"> The resulting parsed arguments. </param>
        /// <returns> true if no errors were detected. </returns>
        public static bool ParseArgumentsWithUsage(string [] arguments, object destination)
        {
            if (Parser.ParseHelp(arguments) || !Parser.ParseArguments(arguments, destination))
            {
                // error encountered in arguments. Display usage message
                System.Console.Write(Parser.ArgumentsUsage(destination.GetType()));
                return false;
            }
            
            return true;
        }

        /// <summary>
        /// Parses Command Line Arguments. 
        /// Errors are output on Console.Error.
        /// Use ArgumentAttributes to control parsing behaviour.
        /// </summary>
        /// <param name="arguments"> The actual arguments. </param>
        /// <param name="destination"> The resulting parsed arguments. </param>
        /// <returns> true if no errors were detected. </returns>
        public static bool ParseArguments(string [] arguments, object destination)
        {
            return Parser.ParseArguments(arguments, destination, new ErrorReporter(Console.Error.WriteLine));
        }
        
        /// <summary>
        /// Parses Command Line Arguments. 
        /// Use ArgumentAttributes to control parsing behaviour.
        /// </summary>
        /// <param name="arguments"> The actual arguments. </param>
        /// <param name="destination"> The resulting parsed arguments. </param>
        /// <param name="reporter"> The destination for parse errors. </param>
        /// <returns> true if no errors were detected. </returns>
        public static bool ParseArguments(string[] arguments, object destination, ErrorReporter reporter)
        {
            Parser parser = new Parser(destination.GetType(), reporter);
            return parser.Parse(arguments, destination);
        }

        private static void NullErrorReporter(string message) 
        { 
        } 

        private class HelpArgument 
        { 
            [ArgumentAttribute(ArgumentType.AtMostOnce, ShortName="?")] 
            public bool help = false; 
        } 

        /// <summary>
        /// Checks if a set of arguments asks for help.
        /// </summary>
        /// <param name="args"> Args to check for help. </param>
        /// <returns> Returns true if args contains /? or /help. </returns>
        public static bool ParseHelp(string[] args)
        {
            Parser helpParser = new Parser(typeof(HelpArgument), new ErrorReporter(NullErrorReporter));
            HelpArgument helpArgument = new HelpArgument();
            helpParser.Parse(args, helpArgument);
            return helpArgument.help;
        }


        /// <summary>
        /// Returns a Usage string for command line argument parsing.
        /// Use ArgumentAttributes to control parsing behaviour.
        /// Formats the output to the width of the current console window.
        /// </summary>
        /// <param name="argumentType"> The type of the arguments to display usage for. </param>
        /// <returns> Printable string containing a user friendly description of command line arguments. </returns>
        public static string ArgumentsUsage(Type argumentType)
        {
            int screenWidth = Parser.GetConsoleWindowWidth();
            if (screenWidth == 0)
                screenWidth = 80;
            return ArgumentsUsage(argumentType, screenWidth);
        }

        /// <summary>
        /// Returns a Usage string for command line argument parsing.
        /// Use ArgumentAttributes to control parsing behaviour.
        /// </summary>
        /// <param name="argumentType"> The type of the arguments to display usage for. </param>
        /// <param name="columns"> The number of columns to format the output to. </param>
        /// <returns> Printable string containing a user friendly description of command line arguments. </returns>
        public static string ArgumentsUsage(Type argumentType, int columns)
        {
            return (new Parser(argumentType, null)).GetUsageString(columns);
        }

        private const int STD_OUTPUT_HANDLE  = -11;

        private struct COORD
        {
            internal Int16 x;
            internal Int16 y;
        }

        private struct SMALL_RECT
        {
            internal Int16 Left;
            internal Int16 Top;
            internal Int16 Right;
            internal Int16 Bottom;
        }

        private struct CONSOLE_SCREEN_BUFFER_INFO
        {
            internal COORD dwSize;
            internal COORD dwCursorPosition;
            internal Int16 wAttributes;
            internal SMALL_RECT srWindow;
            internal COORD dwMaximumWindowSize;
        }

        [DllImport("kernel32.dll", EntryPoint="GetStdHandle", SetLastError=true, CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)]
        private static extern int GetStdHandle(int nStdHandle);

        [DllImport("kernel32.dll", EntryPoint="GetConsoleScreenBufferInfo", SetLastError=true, CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)]
        private static extern int GetConsoleScreenBufferInfo(int hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);

        /// <summary>
        /// Returns the number of columns in the current console window
        /// </summary>
        /// <returns>Returns the number of columns in the current console window</returns>
        public static int GetConsoleWindowWidth()
        {
            int screenWidth;
            CONSOLE_SCREEN_BUFFER_INFO csbi = new CONSOLE_SCREEN_BUFFER_INFO();

            int rc;
            rc = GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), ref csbi);
            screenWidth = csbi.dwSize.x;
            return screenWidth;
        }
        
        /// <summary>
        /// Searches a StringBuilder for a character
        /// </summary>
        /// <param name="text"> The text to search. </param>
        /// <param name="value"> The character value to search for. </param>
        /// <param name="startIndex"> The index to stat searching at. </param>
        /// <returns> The index of the first occurence of value or -1 if it is not found. </returns>
        public static int IndexOf(StringBuilder text, char value, int startIndex)
        {
            for (int index = startIndex; index < text.Length; index++)
            {
                if (text[index] == value)
                    return index;
            }

            return -1;
        }

        /// <summary>
        /// Searches a StringBuilder for a character in reverse
        /// </summary>
        /// <param name="text"> The text to search. </param>
        /// <param name="value"> The character to search for. </param>
        /// <param name="startIndex"> The index to start the search at. </param>
        /// <returns>The index of the last occurence of value in text or -1 if it is not found. </returns>
        public static int LastIndexOf(StringBuilder text, char value, int startIndex)
        {
            for (int index = Math.Min(startIndex, text.Length - 1); index >= 0; index --)
            {
                if (text[index] == value)
                    return index;
            }
            
            return -1;
        }
 
        private const int spaceBeforeParam = 2;

        /// <summary>
        /// Creates a new command line argument parser.
        /// </summary>
        /// <param name="argumentSpecification"> The type of object to  parse. </param>
        /// <param name="reporter"> The destination for parse errors. </param>
        public Parser(Type argumentSpecification, ErrorReporter reporter)
        {
            this.reporter = reporter;
            this.arguments = new ArrayList();
            this.argumentMap = new Hashtable();
            
            foreach (FieldInfo field in argumentSpecification.GetFields())
            {
                if (!field.IsStatic && !field.IsInitOnly && !field.IsLiteral)
                {
                    ArgumentAttribute attribute = GetAttribute(field);
                    if (attribute is DefaultArgumentAttribute)
                    {
                        Debug.Assert(this.defaultArgument == null);
                        this.defaultArgument = new Argument(attribute, field, reporter);
                    }
                    else
                    {
                        this.arguments.Add(new Argument(attribute, field, reporter));
                    }
                }
            }
            
            // add explicit names to map
            foreach (Argument argument in this.arguments)
            {
                Debug.Assert(!argumentMap.ContainsKey(argument.LongName));
                this.argumentMap[argument.LongName] = argument;
                if (argument.ExplicitShortName)
                {
                    if (argument.ShortName != null && argument.ShortName.Length > 0)
                    {
                        Debug.Assert(!argumentMap.ContainsKey(argument.ShortName));
                        this.argumentMap[argument.ShortName] = argument;
                    }
                    else
                    {
                        argument.ClearShortName();
                    }
                }
            }
            
            // add implicit names which don't collide to map
            foreach (Argument argument in this.arguments)
            {
                if (!argument.ExplicitShortName)
                {
                    if (argument.ShortName != null && argument.ShortName.Length > 0 && !argumentMap.ContainsKey(argument.ShortName))
                        this.argumentMap[argument.ShortName] = argument;
                    else
                        argument.ClearShortName();
                }
            }
        }
        
        private static ArgumentAttribute GetAttribute(FieldInfo field)
        {
            object[] attributes = field.GetCustomAttributes(typeof(ArgumentAttribute), false);
            if (attributes.Length == 1)
                return (ArgumentAttribute) attributes[0];

            Debug.Assert(attributes.Length == 0);
            return null;
        }
        
        private void ReportUnrecognizedArgument(string argument)
        {
            this.reporter(string.Format("Unrecognized command line argument '{0}'", argument));
        }
        
        /// <summary>
        /// Parses an argument list into an object
        /// </summary>
        /// <param name="args"></param>
        /// <param name="destination"></param>
        /// <returns> true if an error occurred </returns>
        private bool ParseArgumentList(string[] args, object destination)
        {
            bool hadError = false;
            if (args != null)
            {
                foreach (string argument in args)
                {
                    if (argument.Length > 0)
                    {
                        switch (argument[0])
                        {
                            case '-':
                            case '/':
                                int endIndex = argument.IndexOfAny(new char[] {':', '+', '-'}, 1);
                                string option = argument.Substring(1, endIndex == -1 ? argument.Length - 1 : endIndex - 1);
                                string optionArgument;
                                if (option.Length + 1 == argument.Length)
                                {
                                    optionArgument = null;
                                }
                                else if (argument.Length > 1 + option.Length && argument[1 + option.Length] == ':')
                                {
                                    optionArgument = argument.Substring(option.Length + 2);
                                }
                                else
                                {
                                    optionArgument = argument.Substring(option.Length + 1);
                                }
                                
                                Argument arg = (Argument) this.argumentMap[option];
                                if (arg == null)
                                {
                                    ReportUnrecognizedArgument(argument);
                                    hadError = true;
                                }
                                else
                                {
                                    hadError |= !arg.SetValue(optionArgument, destination);
                                }
                                break;
                            case '@':
                                string[] nestedArguments;
                                hadError |= LexFileArguments(argument.Substring(1), out nestedArguments);
                                hadError |= ParseArgumentList(nestedArguments, destination);
                                break;
                            default:
                                if (this.defaultArgument != null)
                                {
                                    hadError |= !this.defaultArgument.SetValue(argument, destination);
                                }
                                else
                                {
                                    ReportUnrecognizedArgument(argument);
                                    hadError = true;
                                }
                                break;
                        }
                    }
                }
            }
            
            return hadError;
        }
        
        /// <summary>
        /// Parses an argument list.
        /// </summary>
        /// <param name="args"> The arguments to parse. </param>
        /// <param name="destination"> The destination of the parsed arguments. </param>
        /// <returns> true if no parse errors were encountered. </returns>
        public bool Parse(string[] args, object destination)
        {
            bool hadError = ParseArgumentList(args, destination);

            // check for missing required arguments
            foreach (Argument arg in this.arguments)
            {
                hadError |= arg.Finish(destination);
            }
            if (this.defaultArgument != null)
            {
                hadError |= this.defaultArgument.Finish(destination);
            }
            
            return !hadError;
        }

        private struct ArgumentHelpStrings
        {
            public ArgumentHelpStrings(string syntax, string help)
            {
                this.syntax = syntax;
                this.help = help;
            }
            
            public string syntax;
            public string help;
        }
        
        /// <summary>
        /// A user firendly usage string describing the command line argument syntax.
        /// </summary>
        public string GetUsageString(int screenWidth)
        {
            ArgumentHelpStrings[] strings = GetAllHelpStrings();

            int maxParamLen = 0;
            foreach (ArgumentHelpStrings helpString in strings)
            {
                maxParamLen = Math.Max(maxParamLen, helpString.syntax.Length);
            }
            
            const int minimumNumberOfCharsForHelpText = 10;
            const int minimumHelpTextColumn = 5;
            const int minimumScreenWidth = minimumHelpTextColumn + minimumNumberOfCharsForHelpText;

            int helpTextColumn;
            int idealMinimumHelpTextColumn = maxParamLen + spaceBeforeParam;
            screenWidth = Math.Max(screenWidth, minimumScreenWidth);
            if (screenWidth < (idealMinimumHelpTextColumn + minimumNumberOfCharsForHelpText))
                helpTextColumn = minimumHelpTextColumn;
            else
                helpTextColumn = idealMinimumHelpTextColumn;

            const string newLine = "\n";
            StringBuilder builder = new StringBuilder();
            foreach (ArgumentHelpStrings helpStrings in strings)
            {
                // add syntax string
                int syntaxLength = helpStrings.syntax.Length;
                builder.Append(helpStrings.syntax);
                
                // start help text on new line if syntax string is too long
                int currentColumn = syntaxLength;
                if (syntaxLength >= helpTextColumn)
                {
                    builder.Append(newLine);
                    currentColumn = 0;
                }
                
                // add help text broken on spaces
                int charsPerLine = screenWidth - helpTextColumn;
                int index = 0;
                while (index < helpStrings.help.Length)
                {
                    // tab to start column
                    builder.Append(' ', helpTextColumn - currentColumn);
                    currentColumn = helpTextColumn;
                    
                    // find number of chars to display on this line
                    int endIndex = index + charsPerLine;
                    if (endIndex >= helpStrings.help.Length)
                    {
                        // rest of text fits on this line
                        endIndex = helpStrings.help.Length;
                    }
                    else
                    {
                        endIndex = helpStrings.help.LastIndexOf(' ', endIndex - 1, Math.Min(endIndex - index, charsPerLine));
                        if (endIndex <= index)
                        {
                            // no spaces on this line, append full set of chars
                            endIndex = index + charsPerLine;
                        }
                    }
                    
                    // add chars
                    builder.Append(helpStrings.help, index, endIndex - index);
                    index = endIndex;
                    
                    // do new line
                    AddNewLine(newLine, builder, ref currentColumn);

                    // don't start a new line with spaces
                    while (index < helpStrings.help.Length && helpStrings.help[index] == ' ')
                        index ++;
                }

                // add newline if there's no help text                
                if (helpStrings.help.Length == 0)
                {
                    builder.Append(newLine);
                }
            }
            
            return builder.ToString();
        }
        private static void AddNewLine(string newLine, StringBuilder builder, ref int currentColumn)
        {
            builder.Append(newLine);
            currentColumn = 0;
        }
        private ArgumentHelpStrings[] GetAllHelpStrings()
        {
            ArgumentHelpStrings[] strings = new ArgumentHelpStrings[NumberOfParametersToDisplay()];

            int index = 0;
            foreach (Argument arg in this.arguments)
            {
                strings[index] = GetHelpStrings(arg);
                index++;
            }
            strings[index++] = new ArgumentHelpStrings("@<file>", "Read response file for more options");
            if (this.defaultArgument != null)
                strings[index++] = GetHelpStrings(this.defaultArgument);

            return strings;
        }
        
        private static ArgumentHelpStrings GetHelpStrings(Argument arg)
        {
            return new ArgumentHelpStrings(arg.SyntaxHelp, arg.FullHelpText);
        }
        
        private int NumberOfParametersToDisplay()
        {
            int numberOfParameters = this.arguments.Count + 1;
            if (HasDefaultArgument)
                numberOfParameters += 1;
            return numberOfParameters;
        }
        
        /// <summary>
        /// Does this parser have a default argument.
        /// </summary>
        /// <value> Does this parser have a default argument. </value>
        public bool HasDefaultArgument
        {
            get { return this.defaultArgument != null; }
        }

        private bool LexFileArguments(string fileName, out string[] arguments)
        {
            string args  = null;
                    
            try
            {
                using (FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read))
                {
                    args = (new StreamReader(file)).ReadToEnd();
                }
            }
            catch (Exception e)
            {
                this.reporter(string.Format("Error: Can't open command line argument file '{0}' : '{1}'", fileName, e.Message));
                arguments = null;
                return false;
            }

            bool hadError = false;                    
            ArrayList argArray = new ArrayList();
            StringBuilder currentArg = new StringBuilder();
            bool inQuotes = false;
            int index = 0;
            
            // while (index < args.Length)
            try
            {
                while (true)
                {
                    // skip whitespace
                    while (char.IsWhiteSpace(args[index]))
                    {
                        index += 1;
                    }
                    
                    // # - comment to end of line
                    if (args[index] == '#')
                    {
                        index += 1;
                        while (args[index] != '\n')
                        {
                            index += 1;
                        }
                        continue;
                    }
                    
                    // do one argument
                    do
                    {
                        if (args[index] == '\\')
                        {
                            int cSlashes = 1;
                            index += 1;
                            while (index == args.Length && args[index] == '\\')
                            {
                                cSlashes += 1;
                            }

                            if (index == args.Length || args[index] != '"')
                            {
                                currentArg.Append('\\', cSlashes);
                            }
                            else
                            {
                                currentArg.Append('\\', (cSlashes >> 1));
                                if (0 != (cSlashes & 1))
                                {
                                    currentArg.Append('"');
                                }
                                else
                                {
                                    inQuotes = !inQuotes;
                                }
                            }
                        }
                        else if (args[index] == '"')
                        {
                            inQuotes = !inQuotes;
                            index += 1;
                        }
                        else
                        {
                            currentArg.Append(args[index]);
                            index += 1;
                        }
                    } while (!char.IsWhiteSpace(args[index]) || inQuotes);
                    argArray.Add(currentArg.ToString());
                    currentArg.Length = 0;
                }
            }
            catch (System.IndexOutOfRangeException)
            {
                // got EOF 
                if (inQuotes)
                {
                    this.reporter(string.Format("Error: Unbalanced '\"' in command line argument file '{0}'", fileName));
                    hadError = true;
                }
                else if (currentArg.Length > 0)
                {
                    // valid argument can be terminated by EOF
                    argArray.Add(currentArg.ToString());
                }
            }
            
            arguments = (string[]) argArray.ToArray(typeof (string));
            return hadError;
        }
        
        private static string LongName(ArgumentAttribute attribute, FieldInfo field)
        {
            return (attribute == null || attribute.DefaultLongName) ? field.Name : attribute.LongName;
        }
        
        private static string ShortName(ArgumentAttribute attribute, FieldInfo field)
        {
            if (attribute is DefaultArgumentAttribute)
                return null;
            if (!ExplicitShortName(attribute))
                return LongName(attribute, field).Substring(0,1);
            return attribute.ShortName;
        }
        
        private static string HelpText(ArgumentAttribute attribute, FieldInfo field)
        {
            if (attribute == null)
                return null;
            else
                return attribute.HelpText;
        }
        
        private static bool HasHelpText(ArgumentAttribute attribute)
        {
            return (attribute != null && attribute.HasHelpText);
        }
        
        private static bool ExplicitShortName(ArgumentAttribute attribute)
        {
            return (attribute != null && !attribute.DefaultShortName);
        }

        private static object DefaultValue(ArgumentAttribute attribute, FieldInfo field)
        {
            return (attribute == null || !attribute.HasDefaultValue) ? null : attribute.DefaultValue;
        }

        private static Type ElementType(FieldInfo field)
        {
            if (IsCollectionType(field.FieldType))
                return field.FieldType.GetElementType();
            else
                return null;
        }
        
        private static ArgumentType Flags(ArgumentAttribute attribute, FieldInfo field)
        {
            if (attribute != null)
                return attribute.Type;
            else if (IsCollectionType(field.FieldType))
                return ArgumentType.MultipleUnique;
            else
                return ArgumentType.AtMostOnce;
        }
        
        private static bool IsCollectionType(Type type)
        {
            return type.IsArray;
        }
            
        private static bool IsValidElementType(Type type)
        {
            return type != null && (
                type == typeof(int) ||
                type == typeof(uint) ||
                type == typeof(string) ||
                type == typeof(bool) ||
                type.IsEnum);
        }
        
        [System.Diagnostics.DebuggerDisplay("Name = {LongName}")]
        private class Argument
        {
            public Argument(ArgumentAttribute attribute, FieldInfo field, ErrorReporter reporter)
            {
                this.longName = Parser.LongName(attribute, field);
                this.explicitShortName = Parser.ExplicitShortName(attribute);
                this.shortName = Parser.ShortName(attribute, field);
                this.hasHelpText = Parser.HasHelpText(attribute);
                this.helpText = Parser.HelpText(attribute, field);
                this.defaultValue = Parser.DefaultValue(attribute, field);
                this.elementType = ElementType(field);
                this.flags = Flags(attribute, field);
                this.field = field;
                this.seenValue = false;
                this.reporter = reporter;
                this.isDefault = attribute != null && attribute is DefaultArgumentAttribute;
                
                if (IsCollection)
                {
                    this.collectionValues = new ArrayList();
                }
                
                Debug.Assert(this.longName != null && this.longName != "");
                Debug.Assert(!this.isDefault || !this.ExplicitShortName);
                Debug.Assert(!IsCollection || AllowMultiple, "Collection arguments must have allow multiple");
                Debug.Assert(!Unique || IsCollection, "Unique only applicable to collection arguments");
                Debug.Assert(IsValidElementType(Type) ||
                    IsCollectionType(Type));
                Debug.Assert((IsCollection && IsValidElementType(elementType)) ||
                    (!IsCollection && elementType == null));
                Debug.Assert(!(this.IsRequired && this.HasDefaultValue), "Required arguments cannot have default value");
                Debug.Assert(!this.HasDefaultValue || (this.defaultValue.GetType() == field.FieldType), "Type of default value must match field type");
            }
            
            public bool Finish(object destination)
            {
                if (this.SeenValue)
                {
                    if (this.IsCollection)
                    {
                        this.field.SetValue(destination, this.collectionValues.ToArray(this.elementType));
                    }
                }
                else
                {
                    if (this.HasDefaultValue)
                    {
                        this.field.SetValue(destination, this.DefaultValue);
                    }
                }
                
                return ReportMissingRequiredArgument();
            }
            
            private bool ReportMissingRequiredArgument()
            {
                if (this.IsRequired && !this.SeenValue)
                {
                    if (this.IsDefault)
                        reporter(string.Format("Missing required argument '<{0}>'.", this.LongName));
                    else
                        reporter(string.Format("Missing required argument '/{0}'.", this.LongName));
                    return true;
                }
                return false;
            }
            
            private void ReportDuplicateArgumentValue(string value)
            {
                this.reporter(string.Format("Duplicate '{0}' argument '{1}'", this.LongName, value));
            }
            
            public bool SetValue(string value, object destination)
            {
                if (SeenValue && !AllowMultiple)
                {
                    this.reporter(string.Format("Duplicate '{0}' argument", this.LongName));
                    return false;
                }
                this.seenValue = true;
                
                object newValue;
                if (!ParseValue(this.ValueType, value, out newValue))
                    return false;
                if (this.IsCollection)
                {
                    if (this.Unique && this.collectionValues.Contains(newValue))
                    {
                        ReportDuplicateArgumentValue(value);
                        return false;
                    }
                    else
                    {
                        this.collectionValues.Add(newValue);
                    }
                }
                else
                {
                    this.field.SetValue(destination, newValue);
                }
                
                return true;
            }
            
            public Type ValueType
            {
                get { return this.IsCollection ? this.elementType : this.Type; }
            }
            
            private void ReportBadArgumentValue(string value)
            {
                this.reporter(string.Format("'{0}' is not a valid value for the '{1}' command line option", value, this.LongName));
            }
            
            private bool ParseValue(Type type, string stringData, out object value)
            {
                // null is only valid for bool variables
                // empty string is never valid
                if ((stringData != null || type == typeof(bool)) && (stringData == null || stringData.Length > 0))
                {
                    try
                    {
                        if (type == typeof(string))
                        {
                            value = stringData;
                            return true;
                        }
                        else if (type == typeof(bool))
                        {
                            if (stringData == null || stringData == "+")
                            {
                                value = true;
                                return true;
                            }
                            else if (stringData == "-")
                            {
                                value = false;
                                return true;
                            }
                        }
                        else if (type == typeof(int))
                        {
                            value = int.Parse(stringData);
                            return true;
                        }
                        else if (type == typeof(uint))
                        {
                            value = int.Parse(stringData);
                            return true;
                        }
                        else
                        {
                            Debug.Assert(type.IsEnum);

                            bool valid = false;
                            foreach (string name in Enum.GetNames(type))
                            {
                                if (name == stringData)
                                {
                                    valid = true;
                                    break;
                                }
                            }
                            if (valid)
                            {
                                value = Enum.Parse(type, stringData, true);
                                return true;
                            }
                        }
                    }
                    catch
                    {
                        // catch parse errors
                    }
                }
                                
                ReportBadArgumentValue(stringData);
                value = null;
                return false;
            }
            
            private void AppendValue(StringBuilder builder, object value)
            {
                if (value is string || value is int || value is uint || value.GetType().IsEnum)
                {
                    builder.Append(value.ToString());
                }
                else if (value is bool)
                {
                    builder.Append((bool) value ? "+" : "-");
                }
                else
                {
                    bool first = true;
                    foreach (object o in (System.Array) value)
                    {
                        if (!first)
                        {
                            builder.Append(", ");
                        }
                        AppendValue(builder, o);
                        first = false;
                    }
                }
            }
            
            public string LongName
            {
                get { return this.longName; }
            }

            public bool ExplicitShortName
            {
                get { return this.explicitShortName; }
            }
            
            public string ShortName
            {
                get { return this.shortName; }
            }
            
            public bool HasShortName
            {
                get { return this.shortName != null; }
            }
            
            public void ClearShortName()
            {
                this.shortName = null;
            }

            public bool HasHelpText
            {
                get { return this.hasHelpText; }
            }
            
            public string HelpText
            {
                get { return this.helpText; }
            }
            
            public object DefaultValue
            {
                get { return this.defaultValue; }
            }
            
            public bool HasDefaultValue
            {
                get { return null != this.defaultValue; }
            }

            public string FullHelpText
            {
                get {
                    StringBuilder builder = new StringBuilder();
                    if (this.HasHelpText)
                    {
                        builder.Append(this.HelpText);
                    }
                    if (this.HasDefaultValue)
                    {
                        if (builder.Length > 0)
                            builder.Append(" ");
                        builder.Append("Default value:'");
                        AppendValue(builder, this.DefaultValue);
                        builder.Append('\'');
                    }
                    if (this.HasShortName)
                    {
                        if (builder.Length > 0)
                            builder.Append(" ");
                        builder.Append("(short form /");
                        builder.Append(this.ShortName);
                        builder.Append(")");
                    }
                    return builder.ToString();
                }
            }

            public string SyntaxHelp
            {
                get
                {
                    StringBuilder builder = new StringBuilder();

                    if (this.IsDefault)
                    {
                        builder.Append("<");
                        builder.Append(this.LongName);
                        builder.Append(">");
                    }
                    else
                    {
                        builder.Append("/");
                        builder.Append(this.LongName);
                        Type valueType = this.ValueType;
                        if (valueType == typeof(int))
                        {
                            builder.Append(":<int>");
                        }
                        else if (valueType == typeof(uint))
                        {
                            builder.Append(":<uint>");
                        }
                        else if (valueType == typeof(bool))
                        {
                            builder.Append("[+|-]");
                        }
                        else if (valueType == typeof(string))
                        {
                            builder.Append(":<string>");
                        }
                        else
                        {
                            Debug.Assert(valueType.IsEnum);

                            builder.Append(":{");
                            bool first = true;
                            foreach (FieldInfo field in valueType.GetFields())
                            {
                                if (field.IsStatic)
                                {
                                    if (first)
                                        first = false;
                                    else
                                        builder.Append('|');
                                    builder.Append(field.Name);
                                }
                            }
                            builder.Append('}');
                        }
                    }
                    
                    return builder.ToString();
                }
            }

            public bool IsRequired
            {
                get { return 0 != (this.flags & ArgumentType.Required); }
            }
            
            public bool SeenValue
            {
                get { return this.seenValue; }
            }
            
            public bool AllowMultiple
            {
                get { return 0 != (this.flags & ArgumentType.Multiple); }
            }
            
            public bool Unique
            {
                get { return 0 != (this.flags & ArgumentType.Unique); }
            }
            
            public Type Type
            {
                get { return field.FieldType; }
            }
            
            public bool IsCollection
            {
                get { return IsCollectionType(Type); }
            }
            
            public bool IsDefault
            {
                get { return this.isDefault; }
            }
            
            private string longName;
            private string shortName;
            private string helpText;
            private bool hasHelpText;
            private bool explicitShortName;
            private object defaultValue;
            private bool seenValue;
            private FieldInfo field;
            private Type elementType;
            private ArgumentType flags;
            private ArrayList collectionValues;
            private ErrorReporter reporter;
            private bool isDefault;
        }
        
        private ArrayList arguments;
        private Hashtable argumentMap;
        private Argument defaultArgument;
        private ErrorReporter reporter;
    }
}

================================================
FILE: Examples/SingleProcess/Program.cs
================================================
// SharedMemory (File: SingleProcess\Program.cs)
// Copyright (c) 2014 Justin Stenning
// http://spazzarama.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// The SharedMemory library is inspired by the following Code Project article:
//   "Fast IPC Communication Using Shared Memory and InterlockedCompareExchange"
//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using CommandLine;
#if NET40Plus
using System.Threading.Tasks;
#endif

namespace SingleProcess
{
    class Program
    {
        class AppArguments
        {
            [Argument(ArgumentType.AtMostOnce, ShortName = "b", DefaultValue = 1048576, HelpText = "The buffer size.")]
            public int bufferSize = 0;
            [Argument(ArgumentType.AtMostOnce, ShortName = "n", DefaultValue = 50, HelpText = "The number of nodes.")]
            public int nodeCount = 0;
            [Argument(ArgumentType.Required, ShortName = "w", HelpText = "The number of writers.")]
            public int writers = 0;
            [Argument(ArgumentType.Required, ShortName = "r", HelpText = "The number of readers.")]
            public int readers = 0;
            [Argument(ArgumentType.AtMostOnce, ShortName = "e", DefaultValue = 100000, HelpText = "The number of elements to process.")]
            public int elements = 0;
        }

        static void Main(string[] args)
        {
            int elements = 100000;
            int writeCount = 0;
            int clientWaitCount = 0;
            int readCount = 0;
            int serverWaitCount = 0;
            long lastTick = 0;
            int bufferSize = 1048576;
            int size = sizeof(byte) * bufferSize;
            int count = 50; // node count within buffer

            int serverCount = 0;
            int clientCount = 0;

            // Process command line
            AppArguments parsedArgs = new AppArguments();
            var validArgs = Parser.ParseArgumentsWithUsage(args, parsedArgs);

            if (!validArgs)
            {
                return;
            }
            else
            {
                elements = parsedArgs.elements;
                bufferSize = parsedArgs.bufferSize;
                serverCount = parsedArgs.writers;
                clientCount = parsedArgs.readers;
                size = sizeof(byte) * bufferSize;
                count = parsedArgs.nodeCount;
            }

            
            Console.WriteLine("Node buffer size: {0}, count: {1}, writers: {2}, readers {3}, elements: {4}", size, count, serverCount, clientCount, elements);

            int dataListCount = 256;
            // Generate random data to be written
            Random random = new Random();
            byte[][] dataList = new byte[dataListCount][];
            for (var j = 0; j < dataListCount; j++)
            {
                var data = new byte[size];
                random.NextBytes(data);
                dataList[j] = data;
            }

            long bytesWritten = 0;
            long bytesRead = 0;
            string name = Guid.NewGuid().ToString();
            var server = new SharedMemory.CircularBuffer(name, count, size);
            
            Stopwatch sw = Stopwatch.StartNew();

            Action clientAction = () =>
            {
                byte[] testData = new byte[size];

                var client = new SharedMemory.CircularBuffer(name);

                Stopwatch clientTime = new Stopwatch();
                clientTime.Start();
                long startTick = 0;
                long stopTick = 0;

                for (; ; )
                {
                    startTick = clientTime.ElapsedTicks;
                    int amount = client.Read(testData, 100);
                    bytesRead += amount;
                    if (amount == 0)
                        Interlocked.Increment(ref clientWaitCount);
                    else
                        Interlocked.Increment(ref readCount);
                    stopTick = clientTime.ElapsedTicks;

                    if (writeCount > elements && writeCount - readCount == 0)
                        break;
                }
            };
            for (int c = 0; c < clientCount; c++)
            {
#if NET40Plus
                Task c1 = Task.Factory.StartNew(clientAction);
#else
                ThreadPool.QueueUserWorkItem((o) => { clientAction(); });
#endif
            }
            bool wait = true;
            int index = 0;
            Action serverWrite = () =>
            {
                int serverIndex = Interlocked.Increment(ref index);

                var writer = (serverIndex == 1 ? server : new SharedMemory.CircularBuffer(name));
                bool done = false;
                TimeSpan doneTime = TimeSpan.MinValue;
                for (; ; )
                {
                    if (writeCount <= elements)
                    {
                        int amount = writer.Write(dataList[random.Next(0, dataListCount)], 100);
                        bytesWritten += amount;
                        if (amount == 0)
                            Interlocked.Increment(ref serverWaitCount);
                        else
                            Interlocked.Increment(ref writeCount);
                    }
                    else
                    {
                        if (!done && serverIndex == 1)
                        {
                            doneTime = sw.Elapsed;
                            done = true;
                        }
                    }

                    if (serverIndex == 1 && sw.ElapsedTicks - lastTick > 1000000)
                    {
                        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);
                        lastTick = sw.ElapsedTicks;
                        if (writeCount > elements && writeCount - readCount == 0)
                        {
                            Console.WriteLine("Total Time: " + doneTime);
                            wait = false;
                            break;
                        }
                    }
                }
            };

            for (int s = 0; s < serverCount; s++)
            {
#if NET40Plus
                Task s1 = Task.Factory.StartNew(serverWrite);
#else
                ThreadPool.QueueUserWorkItem((o) => { serverWrite(); });
#endif
            }
            while (wait)
                Thread.Sleep(100);
        }
    }
}


================================================
FILE: Examples/SingleProcess/SingleProcess.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>netcoreapp3.0;netcoreapp2.0;net47;net46;net45;net4;net35</TargetFrameworks>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\SharedMemory\SharedMemory.csproj" />
  </ItemGroup>

</Project>


================================================
FILE: LICENSE.md
================================================
SharedMemory

Copyright (c) 2014-2020 Justin Stenning
http://spazzarama.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

The SharedMemory library was inspired by the following Code Project article:
  "Fast IPC Communication Using Shared Memory and InterlockedCompareExchange"
  http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int


================================================
FILE: README.md
================================================
SharedMemory
============

C# shared memory classes for sharing data between processes (Array, Buffer, Circular Buffer and RPC)

[![Build status](https://ci.appveyor.com/api/projects/status/uc32kwm1281y4sie?svg=true)](https://ci.appveyor.com/project/spazzarama/sharedmemory)

About
-----

The 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.

The library uses the .NET MemoryMappedFile class in .NET 4.0+, and implements its own wrapper class for .NET 3.5.

Classes
-------

 * `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.
 * `SharedMemory.BufferWithLocks` - an abstract class that extends SharedMemory.SharedBuffer to provide simple read/write locking support through the use of EventWaitHandles.
 * `SharedMemory.SharedArray` - a simple generic array implementation utilising a shared memory buffer. Inherits from SharedMemory.BufferWithLocks to provide support for thread synchronisation.
 * `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.
 * `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.
 * `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

Example Usage
-------------

The output from the of the following examples is:

    SharedMemory.SharedArray:
    123
    456
    SharedMemory.CircularBuffer:
    123
    456
    SharedMemory.BufferReadWrite:
    123
    456
    SharedMemory.RpcBuffer:
    133

**SharedMemory.SharedArray**

    Console.WriteLine("SharedMemory.SharedArray:");
    using (var producer = new SharedMemory.SharedArray<int>("MySharedArray", 10))
    using (var consumer = new SharedMemory.SharedArray<int>("MySharedArray"))
    {
        producer[0] = 123;
        producer[producer.Length - 1] = 456;
        
        Console.WriteLine(consumer[0]);
        Console.WriteLine(consumer[consumer.Length - 1]);
    }

**SharedMemory.CircularBuffer**

    Console.WriteLine("SharedMemory.CircularBuffer:");
    using (var producer = new SharedMemory.CircularBuffer(name: "MySharedMemory", nodeCount: 3, nodeBufferSize: 4))
    using (var consumer = new SharedMemory.CircularBuffer(name: "MySharedMemory"))
    {
        // nodeCount must be one larger than the number
        // of writes that must fit in the buffer at any one time
        producer.Write<int>(new int[] { 123 });
        producer.Write<int>(new int[] { 456 });
       
        int[] data = new int[1];
        consumer.Read<int>(data);
        Console.WriteLine(data[0]);
        consumer.Read<int>(data);
        Console.WriteLine(data[0]);
    }

**SharedMemory.BufferReadWrite**

    Console.WriteLine("SharedMemory.BufferReadWrite:");
    using (var producer = new SharedMemory.BufferReadWrite(name: "MySharedBuffer", bufferSize: 1024))
    using (var consumer = new SharedMemory.BufferReadWrite(name: "MySharedBuffer"))
    {
        int data = 123;
        producer.Write<int>(ref data);
        data = 456;
        producer.Write<int>(ref data, 1000);
        
        int readData;
        consumer.Read<int>(out readData);
        Console.WriteLine(readData);
        consumer.Read<int>(out readData, 1000);
        Console.WriteLine(readData);
    }

**SharedMemory.RpcBuffer**

    Console.WriteLine("SharedMemory.RpcBuffer:");
    // Ensure a unique channel name
    var rpcName = "RpcTest" + Guid.NewGuid().ToString();
    var rpcMaster = new RpcBuffer(rpcName);
    var rpcSlave = new RpcBuffer(rpcName, (msgId, payload) =>
    {
        // Add the two bytes together
        return BitConverter.GetBytes((payload[0] + payload[1]));
    });
    
    // Call the remote handler to add 123 and 10
    var result = rpcMaster.RemoteRequest(new byte[] { 123, 10 });
    Console.WriteLine(result); // outputs 133

Performance
-----------

### RPC Buffer

When 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`).

If 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).

For 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.

A 1KB message can be sent as a single packet approximately 10k times/sec.

### Circular Buffer

The 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.

The 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.

![Circular buffer bandwidth](http://spazzarama.com/wp-content/uploads/2015/12/SharedMemoryBandwidth.png)

All 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.


================================================
FILE: SharedMemory/BufferReadWrite.cs
================================================
// SharedMemory (File: SharedMemory\bufferreadwrite.cs)
// Copyright (c) 2014 Justin Stenning
// http://spazzarama.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// The SharedMemory library is inspired by the following Code Project article:
//   "Fast IPC Communication Using Shared Memory and InterlockedCompareExchange"
//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text;

namespace SharedMemory
{
    /// <summary>
    /// Read/Write buffer with support for simple inter-process read/write synchronisation.
    /// </summary>
#if NETFULL
    [PermissionSet(SecurityAction.LinkDemand)]
#endif
    public unsafe class BufferReadWrite : BufferWithLocks
    {
        #region Constructors

        /// <summary>
        /// Creates a new shared memory buffer with the specified name and size
        /// </summary>
        /// <param name="name">The name of the shared memory to create</param>
        /// <param name="bufferSize">The size of the buffer</param>
        public BufferReadWrite(string name, int bufferSize)
            : base(name, bufferSize, true)
        {
            Open();
        }

        /// <summary>
        /// Opens an existing shared memory buffer with the specified name
        /// </summary>
        /// <param name="name">The name of the shared memory to open</param>
        public BufferReadWrite(string name)
            : base(name, 0, false)
        {
            Open();
        }

        #endregion

        #region Writing

        /// <summary>
        /// Writes an instance of <typeparamref name="T"/> into the buffer
        /// </summary>
        /// <typeparam name="T">A structure type</typeparam>
        /// <param name="data">A reference to an instance of <typeparamref name="T"/> to be written</param>
        /// <param name="bufferPosition">The offset within the buffer region of the shared memory to write to.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1061:DoNotHideBaseClassMethods")]
        new public void Write<T>(ref T data, long bufferPosition = 0)
            where T : struct
        {
            base.Write(ref data, bufferPosition);
        }

        /// <summary>
        /// Writes an array of <typeparamref name="T"/> into the buffer
        /// </summary>
        /// <typeparam name="T">A structure type</typeparam>
        /// <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>
        /// <param name="bufferPosition">The offset within the buffer region of the shared memory to write to.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1061:DoNotHideBaseClassMethods")]
        new public void Write<T>(T[] buffer, long bufferPosition = 0)
            where T : struct
        {
            base.Write(buffer, bufferPosition);
        }

        /// <summary>
        /// Writes <paramref name="length"/> bytes from the <paramref name="ptr"/> into the shared memory buffer.
        /// </summary>
        /// <param name="ptr">A managed pointer to the memory location to be copied into the buffer</param>
        /// <param name="length">The number of bytes to be copied</param>
        /// <param name="bufferPosition">The offset within the buffer region of the shared memory to write to.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1061:DoNotHideBaseClassMethods")]
        new public void Write(IntPtr ptr, int length, long bufferPosition = 0)
        {
            base.Write(ptr, length, bufferPosition);
        }

        /// <summary>
        /// Prepares an IntPtr to the buffer position and calls <paramref name="writeFunc"/> to perform the writing.
        /// </summary>
        /// <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>
        /// <param name="bufferPosition">The offset within the buffer region to start writing from.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1061:DoNotHideBaseClassMethods")]
        new public void Write(Action<IntPtr> writeFunc, long bufferPosition = 0)
        {
            base.Write(writeFunc, bufferPosition);
        }

        #endregion

        #region Reading

        /// <summary>
        /// Reads an instance of <typeparamref name="T"/> from the buffer
        /// </summary>
        /// <typeparam name="T">A structure type</typeparam>
        /// <param name="data">Output parameter that will contain the value read from the buffer</param>
        /// <param name="bufferPosition">The offset within the buffer region of the shared memory to read from.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1061:DoNotHideBaseClassMethods")]
        new public void Read<T>(out T data, long bufferPosition = 0)
            where T : struct
        {
            base.Read(out data, bufferPosition);
        }

        /// <summary>
        /// Reads an array of <typeparamref name="T"/> from the buffer
        /// </summary>
        /// <typeparam name="T">A structure type</typeparam>
        /// <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>
        /// <param name="bufferPosition">The offset within the buffer region of the shared memory to read from.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1061:DoNotHideBaseClassMethods")]
        new public void Read<T>(T[] buffer, long bufferPosition = 0)
            where T : struct
        {
            base.Read(buffer, bufferPosition);
        }

        /// <summary>
        /// Reads <paramref name="length"/> bytes into the memory location <paramref name="destination"/> from the shared memory buffer.
        /// </summary>
        /// <param name="destination">A managed pointer to the memory location to copy data into from the buffer</param>
        /// <param name="length">The number of bytes to be copied</param>
        /// <param name="bufferPosition">The offset within the buffer region of the shared memory to read from.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1061:DoNotHideBaseClassMethods")]
        new public void Read(IntPtr destination, int length, long bufferPosition = 0)
        {
            base.Read(destination, length, bufferPosition);
        }

        /// <summary>
        /// Prepares an IntPtr to the buffer position and calls <paramref name="readFunc"/> to perform the reading.
        /// </summary>
        /// <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>
        /// <param name="bufferPosition">The offset within the buffer region of the shared memory to read from.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1061:DoNotHideBaseClassMethods")]
        new public void Read(Action<IntPtr> readFunc, long bufferPosition = 0)
        {
            base.Read(readFunc, bufferPosition);
        }

        #endregion
    }
}


================================================
FILE: SharedMemory/BufferWithLocks.cs
================================================
// SharedMemory (File: SharedMemory\BufferWithLocks.cs)
// Copyright (c) 2014 Justin Stenning
// http://spazzarama.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// The SharedMemory library is inspired by the following Code Project article:
//   "Fast IPC Communication Using Shared Memory and InterlockedCompareExchange"
//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text;
using System.Threading;

namespace SharedMemory
{
    /// <summary>
    /// <para>Extends <see cref="SharedBuffer"/> to support simple thread-synchronisation for read/write 
    /// to the buffer by allowing callers to acquire and release read/write locks.</para>
    /// <para>All buffer read/write operations have been overloaded to first perform a <see cref="System.Threading.WaitHandle.WaitOne()"/> 
    /// using the <see cref="ReadWaitEvent"/> and <see cref="WriteWaitEvent"/> respectively.</para>
    /// <para>By default all read/write operations will not block, it is necessary to first acquire locks 
    /// through calls to <see cref="AcquireReadLock"/> and <see cref="AcquireWriteLock"/> as appropriate, with corresponding 
    /// calls to <see cref="ReleaseReadLock"/> and <see cref="ReleaseWriteLock"/> to release the locks.</para>
    /// </summary>
#if NETFULL
    [PermissionSet(SecurityAction.LinkDemand)]
    [PermissionSet(SecurityAction.InheritanceDemand)]
#endif
    public abstract class BufferWithLocks : SharedBuffer
    {
        /// <summary>
        /// An event handle used for blocking write operations.
        /// </summary>
        protected EventWaitHandle WriteWaitEvent { get; private set; }
        
        /// <summary>
        /// An event handle used for blocking read operations.
        /// </summary>
        protected EventWaitHandle ReadWaitEvent { get; private set; }

        #region Constructors

        /// <summary>
        /// Create a new <see cref="BufferWithLocks"/> instance with the specified name and buffer size.
        /// </summary>
        /// <param name="name">The name of the shared memory</param>
        /// <param name="bufferSize">The buffer size in bytes.</param>
        /// <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>
        protected BufferWithLocks(string name, long bufferSize, bool ownsSharedMemory)
            : base(name, bufferSize, ownsSharedMemory)
        {
            WriteWaitEvent = new EventWaitHandle(true, EventResetMode.ManualReset, Name + "_evt_write");
            ReadWaitEvent = new EventWaitHandle(true, EventResetMode.ManualReset, Name + "_evt_read");
        }

        #endregion

        #region Synchronisation

        private int _readWriteTimeout = 100;

        /// <summary>
        /// The Read/Write operation timeout in milliseconds (to prevent deadlocks). Defaults to 100ms and must be larger than -1.
        /// If a Read or Write operation's WaitEvent does not complete within this timeframe a <see cref="TimeoutException"/> will be thrown.
        /// If using AcquireReadLock/ReleaseReadLock and AcquireWriteLock/ReleaseWriteLock correctly this timeout will never occur.
        /// </summary>
        public virtual int ReadWriteTimeout
        {
            get { return _readWriteTimeout; }
            set
            {
                if (value < 0)
                    throw new ArgumentOutOfRangeException("ReadWriteTimeout", "Must be larger than -1.");
                _readWriteTimeout = value;
            }
        }

        /// <summary>
        /// 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"/>.
        /// </summary>
        /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite" /> (-1) to wait indefinitely.</param>
        /// <returns>true if the read lock was able to be acquired, otherwise false.</returns>
        /// <exception cref="System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an infinite time-out.</exception>
        /// <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>
        public bool AcquireReadLock(int millisecondsTimeout = System.Threading.Timeout.Infinite)
        {
            if (!ReadWaitEvent.WaitOne(millisecondsTimeout))
                return false;
            WriteWaitEvent.Reset();
            return true;
        }

        /// <summary>
        /// Releases the current read lock, allowing all blocked writes to continue.
        /// </summary>
        public void ReleaseReadLock()
        {
            WriteWaitEvent.Set();
        }

        /// <summary>
        /// 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"/>.
        /// </summary>
        /// <param name="millisecondsTimeout">The number of milliseconds to wait, or System.Threading.Timeout.Infinite (-1) to wait indefinitely.</param>
        /// <returns>true if the write lock was able to be acquired, otherwise false.</returns>
        /// <exception cref="System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an infinite time-out.</exception>
        /// <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>
        public bool AcquireWriteLock(int millisecondsTimeout = System.Threading.Timeout.Infinite)
        {
            if (!WriteWaitEvent.WaitOne(millisecondsTimeout))
                return false;
            ReadWaitEvent.Reset();
            return true;
        }

        /// <summary>
        /// Releases the current write lock, allowing all blocked reads to continue.
        /// </summary>
        public void ReleaseWriteLock()
        {
            ReadWaitEvent.Set();
        }

        #endregion

        #region Writing

        /// <summary>
        /// Prevents write operations from deadlocking by throwing a TimeoutException if the WriteWaitEvent is not available within <see cref="ReadWriteTimeout"/> milliseconds
        /// </summary>
        private void WriteWait()
        {
            if (!WriteWaitEvent.WaitOne(ReadWriteTimeout))
                throw new TimeoutException("The write operation timed out waiting for the write lock WaitEvent. Check your usage of AcquireWriteLock/ReleaseWriteLock and AcquireReadLock/ReleaseReadLock.");
        }

        /// <summary>
        /// Writes an instance of <typeparamref name="T"/> into the buffer
        /// </summary>
        /// <typeparam name="T">A structure type</typeparam>
        /// <param name="data">A reference to an instance of <typeparamref name="T"/> to be written</param>
        /// <param name="bufferPosition">The offset within the buffer region of the shared memory to write to.</param>
        protected override void Write<T>(ref T data, long bufferPosition = 0)
        {
            WriteWait();
            base.Write<T>(ref data, bufferPosition);
        }

        /// <summary>
        /// Writes an array of <typeparamref name="T"/> into the buffer
        /// </summary>
        /// <typeparam name="T">A structure type</typeparam>
        /// <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>
        /// <param name="bufferPosition">The offset within the buffer region of the shared memory to write to.</param>
        protected override void Write<T>(T[] buffer, long bufferPosition = 0)
        {
            WriteWait();
            base.Write<T>(buffer, bufferPosition);
        }

        /// <summary>
        /// Writes <paramref name="length"/> bytes from the <paramref name="ptr"/> into the shared memory buffer.
        /// </summary>
        /// <param name="ptr">A managed pointer to the memory location to be copied into the buffer</param>
        /// <param name="length">The number of bytes to be copied</param>
        /// <param name="bufferPosition">The offset within the buffer region of the shared memory to write to.</param>
        protected override void Write(IntPtr ptr, int length, long bufferPosition = 0)
        {
            WriteWait();
            base.Write(ptr, length, bufferPosition);
        }

        /// <summary>
        /// Prepares an IntPtr to the buffer position and calls <paramref name="writeFunc"/> to perform the writing.
        /// </summary>
        /// <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>
        /// <param name="bufferPosition">The offset within the buffer region to start writing from.</param>
        protected override void Write(Action<IntPtr> writeFunc, long bufferPosition = 0)
        {
            WriteWait();
            base.Write(writeFunc, bufferPosition);
        }

        #endregion

        #region Reading

        /// <summary>
        /// Prevents read operations from deadlocking by throwing a TimeoutException if the ReadWaitEvent is not available within <see cref="ReadWriteTimeout"/> milliseconds
        /// </summary>
        private void ReadWait()
        {
            if (!ReadWaitEvent.WaitOne(ReadWriteTimeout))
                throw new TimeoutException("The read operation timed out waiting for the read lock WaitEvent. Check your usage of AcquireWriteLock/ReleaseWriteLock and AcquireReadLock/ReleaseReadLock.");
        }

        /// <summary>
        /// Reads an instance of <typeparamref name="T"/> from the buffer
        /// </summary>
        /// <typeparam name="T">A structure type</typeparam>
        /// <param name="data">Output parameter that will contain the value read from the buffer</param>
        /// <param name="bufferPosition">The offset within the buffer region of the shared memory to read from.</param>
        protected override void Read<T>(out T data, long bufferPosition = 0)
        {
            ReadWait();
            base.Read<T>(out data, bufferPosition);
        }

        /// <summary>
        /// Reads an array of <typeparamref name="T"/> from the buffer
        /// </summary>
        /// <typeparam name="T">A structure type</typeparam>
        /// <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>
        /// <param name="bufferPosition">The offset within the buffer region of the shared memory to read from.</param>
        protected override void Read<T>(T[] buffer, long bufferPosition = 0)
        {
            ReadWait();
            base.Read<T>(buffer, bufferPosition);
        }

        /// <summary>
        /// Reads <paramref name="length"/> bytes into the memory location <paramref name="destination"/> from the shared memory buffer.
        /// </summary>
        /// <param name="destination">A managed pointer to the memory location to copy data into from the buffer</param>
        /// <param name="length">The number of bytes to be copied</param>
        /// <param name="bufferPosition">The offset within the buffer region of the shared memory to read from.</param>
        protected override void Read(IntPtr destination, int length, long bufferPosition = 0)
        {
            ReadWait();
            base.Read(destination, length, bufferPosition);
        }

        /// <summary>
        /// Prepares an IntPtr to the buffer position and calls <paramref name="readFunc"/> to perform the reading.
        /// </summary>
        /// <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>
        /// <param name="bufferPosition">The offset within the buffer region of the shared memory to read from.</param>
        protected override void Read(Action<IntPtr> readFunc, long bufferPosition = 0)
        {
            ReadWait();
            base.Read(readFunc, bufferPosition);
        }

        #endregion

        #region IDisposable

        /// <summary>
        /// IDisposable pattern
        /// </summary>
        /// <param name="disposeManagedResources">true to release managed resources</param>
        protected override void Dispose(bool disposeManagedResources)
        {
            if (disposeManagedResources)
            {
                (WriteWaitEvent as IDisposable).Dispose();
                (ReadWaitEvent as IDisposable).Dispose();
            }
            base.Dispose(disposeManagedResources);
        }

        #endregion
    }
}


================================================
FILE: SharedMemory/CircularBuffer.cs
================================================
// SharedMemory (File: SharedMemory\CircularBuffer.cs)
// Copyright (c) 2014 Justin Stenning
// http://spazzarama.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// The SharedMemory library is inspired by the following Code Project article:
//   "Fast IPC Communication Using Shared Memory and InterlockedCompareExchange"
//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int

using System;
using System.Collections.Generic;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text;
using System.Threading;

namespace SharedMemory
{
    /// <summary>
    /// A lock-free FIFO shared memory circular buffer (or ring buffer) utilising a <see cref="MemoryMappedFile"/>.
    /// </summary>
#if NETFULL
    [PermissionSet(SecurityAction.LinkDemand)]
    [PermissionSet(SecurityAction.InheritanceDemand)]
#endif
    public unsafe class CircularBuffer : SharedBuffer
    {
        #region Public/Protected properties
        
        /// <summary>
        /// The number of nodes within the circular linked-list
        /// </summary>
        public int NodeCount { get; private set; }
        
        /// <summary>
        /// The buffer size of each node
        /// </summary>
        public int NodeBufferSize { get; private set; }
        
        /// <summary>
        /// Event signaled when data has been written if the reading index has caught up to the writing index
        /// </summary>
        protected EventWaitHandle DataExists { get; set; }

        /// <summary>
        /// Event signaled when a node becomes available after reading if the writing index has caught up to the reading index
        /// </summary>
        protected EventWaitHandle NodeAvailable { get; set; }

        /// <summary>
        /// The offset relative to <see cref="SharedBuffer.BufferStartPtr"/> where the node header starts within the buffer region of the shared memory
        /// </summary>
        protected virtual long NodeHeaderOffset
        {
            get
            {
                return 0;
            }
        }
            
        /// <summary>
        /// Where the linked-list nodes are located within the buffer
        /// </summary>
        protected virtual long NodeOffset
        {
            get
            {
                return NodeHeaderOffset + Marshal.SizeOf(typeof(NodeHeader));
            }
        }

        /// <summary>
        /// Where the list of buffers are located within the shared memory
        /// </summary>
        protected virtual long NodeBufferOffset
        {
            get
            {
                return NodeOffset + (Marshal.SizeOf(typeof(Node)) * NodeCount);
            }
        }

        /// <summary>
        /// Provide direct access to the Node[] memory
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        protected virtual Node* this[int i]
        {
            get
            {
                if (i < 0 || i >= NodeCount)
                    throw new ArgumentOutOfRangeException();

                return ((Node*)(BufferStartPtr + NodeOffset)) + i;
            }
        }

        #endregion

        #region Private field members

        private NodeHeader* _nodeHeader = null;
        
        #endregion

        #region Structures

        /// <summary>
        /// Provides cursors for the circular buffer along with dimensions
        /// </summary>
        /// <remarks>This structure is the same size on 32-bit and 64-bit architectures.</remarks>
        [StructLayout(LayoutKind.Sequential)]
        public struct NodeHeader
        {
            /// <summary>
            /// The index of the first unreadable node
            /// </summary>
            public volatile int ReadEnd;
            /// <summary>
            /// The index of the next readable node
            /// </summary>
            public volatile int ReadStart;

            /// <summary>
            /// The index of the first unwritable node
            /// </summary>
            public volatile int WriteEnd;
            /// <summary>
            /// The index of the next writable node
            /// </summary>
            public volatile int WriteStart;

            /// <summary>
            /// The number of nodes within the buffer
            /// </summary>
            public int NodeCount;

            /// <summary>
            /// The size of the buffer for each node
            /// </summary>
            public int NodeBufferSize;
        }

        /// <summary>
        /// Represents a node within the buffer's circular linked list
        /// </summary>
        /// <remarks>This structure is the same size on 32-bit and 64-bit architectures.</remarks>
        [StructLayout(LayoutKind.Sequential)]
        public struct Node
        {
            /// <summary>
            /// The previous node.
            /// </summary>
            public int Next;

            /// <summary>
            /// The next node.
            /// </summary>
            public int Prev;

            /// <summary>
            /// A flag used while returning a node for writing after having been read.
            /// </summary>
            public volatile int DoneRead;

            /// <summary>
            /// A flag used while posting a node for reading after writing is completed.
            /// </summary>
            public volatile int DoneWrite;

            /// <summary>
            /// Represents the offset relative to <see cref="SharedBuffer.BufferStartPtr"/> where the data for this node can be found.
            /// </summary>
            public long Offset;
            
            /// <summary>
            /// Represents the index of the current node.
            /// </summary>
            public int Index;

            /// <summary>
            /// Holds the number of bytes written into this node.
            /// </summary>
            public int AmountWritten;
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Creates and opens a new <see cref="CircularBuffer"/> instance with the specified name, node count and buffer size per node.
        /// </summary>
        /// <param name="name">The name of the shared memory to be created</param>
        /// <param name="nodeCount">The number of nodes within the circular linked-list (minimum of 2)</param>
        /// <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>
        /// <remarks>
        /// <para>The maximum total shared memory size is dependent upon the system and current memory fragmentation.</para>
        /// <para>The shared memory layout on 32-bit and 64-bit architectures is:<br />
        /// <code>
        /// |       Header       |   NodeHeader  | Node[0] | ... | Node[N-1] | buffer[0] | ... | buffer[N-1] |<br />
        /// |      16-bytes      |    24-bytes   |       32-bytes * N        |     NodeBufferSize * N        |<br />
        ///                      |------------------------------BufferSize-----------------------------------|<br />
        /// |-----------------------------------------SharedMemorySize---------------------------------------|
        /// </code>
        /// </para>
        /// </remarks>
        public CircularBuffer(string name, int nodeCount, int nodeBufferSize)
            : this(name, nodeCount, nodeBufferSize, true)
        {
            Open();
        }

        /// <summary>
        /// Opens an existing <see cref="CircularBuffer"/> with the specified name.
        /// </summary>
        /// <param name="name">The name of an existing <see cref="CircularBuffer"/> previously created with <see cref="SharedBuffer.IsOwnerOfSharedMemory"/>=true</param>
        public CircularBuffer(string name)
            : this(name, 0, 0, false)
        {
            Open();
        }

        private CircularBuffer(string name, int nodeCount, int nodeBufferSize, bool ownsSharedMemory)
            : base(name, Marshal.SizeOf(typeof(NodeHeader)) + (Marshal.SizeOf(typeof(Node)) * nodeCount) + (nodeCount * (long)nodeBufferSize), ownsSharedMemory)
        {
            #region Argument validation
            if (ownsSharedMemory && nodeCount < 2)
                throw new ArgumentOutOfRangeException("nodeCount", nodeCount, "The node count must be a minimum of 2.");
#if DEBUG
            else if (!ownsSharedMemory && (nodeCount != 0 || nodeBufferSize > 0))
                System.Diagnostics.Debug.Write("Node count and nodeBufferSize are ignored when opening an existing shared memory circular buffer.", "Warning");
#endif
            #endregion

            if (IsOwnerOfSharedMemory)
            {
                NodeCount = nodeCount;
                NodeBufferSize = nodeBufferSize;
            }
        }

        #endregion

        #region Open / Close

        /// <summary>
        /// Attempts to create the <see cref="EventWaitHandle"/> handles and initialise the node header and buffers.
        /// </summary>
        /// <returns>True if the events and nodes were initialised successfully.</returns>
        protected override bool DoOpen()
        {
            // Create signal events
            DataExists = new EventWaitHandle(false, EventResetMode.AutoReset, Name + "_evt_dataexists");
            NodeAvailable = new EventWaitHandle(false, EventResetMode.AutoReset, Name + "_evt_nodeavail");

            if (IsOwnerOfSharedMemory)
            {
                // Retrieve pointer to node header
                _nodeHeader = (NodeHeader*)(BufferStartPtr + NodeHeaderOffset);

                // Initialise the node header
                InitialiseNodeHeader();

                // Initialise nodes entries
                InitialiseLinkedListNodes();
            }
            else
            {
                // Load the NodeHeader
                _nodeHeader = (NodeHeader*)(BufferStartPtr + NodeHeaderOffset);
                NodeCount = _nodeHeader->NodeCount;
                NodeBufferSize = _nodeHeader->NodeBufferSize;
            }

            return true;
        }

        /// <summary>
        /// Initialises the node header within the shared memory. Only applicable if <see cref="SharedBuffer.IsOwnerOfSharedMemory"/> is true.
        /// </summary>
        private void InitialiseNodeHeader()
        {
            if (!IsOwnerOfSharedMemory)
                return;

            NodeHeader header = new NodeHeader();
            header.ReadStart = 0;
            header.ReadEnd = 0;
            header.WriteEnd = 0;
            header.WriteStart = 0;
            header.NodeBufferSize = NodeBufferSize;
            header.NodeCount = NodeCount;
            base.Write<NodeHeader>(ref header, NodeHeaderOffset);
        }

        /// <summary>
        /// Initialise the nodes of the circular linked-list. Only applicable if <see cref="SharedBuffer.IsOwnerOfSharedMemory"/> is true.
        /// </summary>
        private void InitialiseLinkedListNodes()
        {
            if (!IsOwnerOfSharedMemory)
                return;

            int N = 0;

            Node[] nodes = new Node[NodeCount];

            // First node
            nodes[N].Next = 1;
            nodes[N].Prev = NodeCount - 1;
            nodes[N].Offset = NodeBufferOffset;
            nodes[N].Index = N;
            // Middle nodes
            for (N = 1; N < NodeCount - 1; N++)
            {
                nodes[N].Next = N + 1;
                nodes[N].Prev = N - 1;
                nodes[N].Offset = NodeBufferOffset + (NodeBufferSize * N);
                nodes[N].Index = N;
            }
            // Last node
            nodes[N].Next = 0;
            nodes[N].Prev = NodeCount - 2;
            nodes[N].Offset = NodeBufferOffset + (NodeBufferSize * N);
            nodes[N].Index = N;

            // Write the nodes to the shared memory
            base.WriteArray<Node>(nodes, 0, nodes.Length, NodeOffset);
        }

        /// <summary>
        /// Closes the events. The shared memory could still be open within one or more other instances.
        /// </summary>
        protected override void DoClose()
        {
            if (DataExists != null)
            {
                (DataExists as IDisposable).Dispose();
                DataExists = null;
                (NodeAvailable as IDisposable).Dispose();
                NodeAvailable = null;
            }

            _nodeHeader = null;
        }

        #endregion

        #region Node Writing

        /// <summary>
        /// Attempts to reserve a node from the linked-list for writing with the specified timeout.
        /// </summary>
        /// <param name="timeout">The number of milliseconds to wait if a node is not immediately available for writing.</param>
        /// <returns>An unsafe pointer to the node if successful, otherwise null</returns>
        protected virtual Node* GetNodeForWriting(int timeout)
        {
            for (; ; )
            {
                int blockIndex = _nodeHeader->WriteStart;
                Node* node = this[blockIndex];
                if (node->Next == _nodeHeader->ReadEnd)
                {
                    // No room is available, wait for room to become available
                    if (NodeAvailable.WaitOne(timeout))
                        continue;

                    // Timeout
                    return null;
                }

#pragma warning disable 0420 // ignore ref to volatile warning - Interlocked API
                if (Interlocked.CompareExchange(ref _nodeHeader->WriteStart, node->Next, blockIndex) == blockIndex)
                    return node;
#pragma warning restore 0420

                // Another thread has already acquired this node for writing, try again.
                continue;
            }
        }

        /// <summary>
        /// Makes a node available for reading after writing is complete
        /// </summary>
        /// <param name="node">An unsafe pointer to the node to return</param>
        protected virtual void PostNode(Node* node)
        {
            // Set the write flag for this node (the node is reserved so no need for locks)
            node->DoneWrite = 1;

            // Move the write pointer as far forward as we can
            // always starting from WriteEnd to make all contiguous
            // completed nodes available for reading.
            for (; ; )
            {
                int blockIndex = _nodeHeader->WriteEnd;
                node = this[blockIndex];
#pragma warning disable 0420 // ignore ref to volatile warning - Interlocked API
                if (Interlocked.CompareExchange(ref node->DoneWrite, 0, 1) != 1)
                {
                    // If we get here then another thread either another thread
                    // has already moved the write index or we have moved forward 
                    // as far as we can
                    return;
                }

                // Move the pointer one forward
                Interlocked.CompareExchange(ref _nodeHeader->WriteEnd, node->Next, blockIndex);
#pragma warning restore 0420

                // Signal the "data exists" event if read threads are waiting
                if (blockIndex == _nodeHeader->ReadStart)
                    DataExists.Set();
            }
        }

        /// <summary>
        /// Writes the byte array buffer to the next available node for writing
        /// </summary>
        /// <param name="source">Reference to the buffer to write</param>
        /// <param name="startIndex">The index within the buffer to start writing from</param>
        /// <param name="timeout">The maximum number of milliseconds to wait for a node to become available for writing (default 1000ms)</param>
        /// <returns>The number of bytes written</returns>
        /// <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>
        public virtual int Write(byte[] source, int startIndex = 0, int timeout = 1000)
        {
            // Grab a node for writing
            Node* node = GetNodeForWriting(timeout);
            if (node == null) return 0;

            // Copy the data
            int amount = Math.Min(source.Length - startIndex, NodeBufferSize);
            
            Marshal.Copy(source, startIndex, new IntPtr(BufferStartPtr + node->Offset), amount);
            node->AmountWritten = amount;
            

            // Writing is complete, make readable
            PostNode(node);

            return amount;
        }

        /// <summary>
        /// Writes the structure array buffer to the next available node for writing
        /// </summary>
        /// <param name="source">Reference to the buffer to write</param>
        /// <param name="startIndex">The index within the buffer to start writing from</param>
        /// <param name="timeout">The maximum number of milliseconds to wait for a node to become available for writing (default 1000ms)</param>
        /// <returns>The number of elements written</returns>
        /// <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>        
        public virtual int Write<T>(T[] source, int startIndex = 0, int timeout = 1000)
            where T : struct
        {
            // Grab a node for writing
            Node* node = GetNodeForWriting(timeout);
            if (node == null) return 0;

            // Write the data using the FastStructure class (much faster than the MemoryMappedViewAccessor WriteArray<T> method)
            int count = Math.Min(source.Length - startIndex, NodeBufferSize / FastStructure.SizeOf<T>());
            base.WriteArray<T>(source, startIndex, count, node->Offset);
            node->AmountWritten = count * FastStructure.SizeOf<T>();

            // Writing is complete, make node readable
            PostNode(node);

            return count;
        }

        /// <summary>
        /// Writes the structure to the next available node for writing
        /// </summary>
        /// <typeparam name="T">The structure type to be written</typeparam>
        /// <param name="source">The structure to be written</param>
        /// <param name="timeout">The maximum number of milliseconds to wait for a node to become available for writing (default 1000ms)</param>
        /// <returns>The number of bytes written - larger than 0 if successful</returns>
        /// <exception cref="ArgumentOutOfRangeException">If the size of the <typeparamref name="T"/> structure is larger than <see cref="NodeBufferSize"/>.</exception>
        public virtual int Write<T>(ref T source, int timeout = 1000)
            where T : struct
        {
            int structSize = Marshal.SizeOf(typeof(T));
            if (structSize > NodeBufferSize)
                throw new ArgumentOutOfRangeException("T", "The size of structure " + typeof(T).Name + " is larger than NodeBufferSize");

            // Attempt to retrieve a node for writing
            Node* node = GetNodeForWriting(timeout);
            if (node == null) return 0;

            // Copy the data using the MemoryMappedViewAccessor
            base.Write<T>(ref source, node->Offset);
            node->AmountWritten = structSize;

            // Return the node for further writing
            PostNode(node);

            return structSize;
        }

        /// <summary>
        /// Writes <paramref name="length"/> bytes from <paramref name="source"/> to the next available node for writing
        /// </summary>
        /// <param name="source">Pointer to the buffer to copy</param>
        /// <param name="timeout">The maximum number of milliseconds to wait for a node to become available (default 1000ms)</param>
        /// <param name="length">The number of bytes to attempt to write</param>
        /// <returns>The number of bytes written</returns>
        /// <remarks>The maximum number of bytes that can be written is the minimum of <paramref name="length"/> and <see cref="NodeBufferSize"/>.</remarks>        
        public virtual int Write(IntPtr source, int length, int timeout = 1000)
        {
            // Grab a node for writing
            Node* node = GetNodeForWriting(timeout);
            if (node == null) return 0;

            // Copy the data
            int amount = Math.Min(length, NodeBufferSize);
            base.Write(source, amount, node->Offset);
            node->AmountWritten = amount;

            // Writing is complete, make readable
            PostNode(node);

            return amount;
        }

        /// <summary>
        /// Reserves a node for writing and then calls the provided <paramref name="writeFunc"/> to perform the write operation.
        /// </summary>
        /// <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. 
        /// The provided function should return the number of bytes written.</param>
        /// <param name="timeout">The maximum number of milliseconds to wait for a node to become available for writing (default 1000ms)</param>
        /// <returns>The number of bytes written</returns>
        public virtual int Write(Func<IntPtr, int> writeFunc, int timeout = 1000)
        {
            // Grab a node for writing
            Node* node = GetNodeForWriting(timeout);
            if (node == null) return 0;

            int amount = 0;
            try
            {
                // Pass destination IntPtr to custom write function
                amount = writeFunc(new IntPtr(BufferStartPtr + node->Offset));
                node->AmountWritten = amount;
            }
            finally
            {
                // Writing is complete, make readable
                PostNode(node);
            }

            return amount;
        }

        #endregion

        #region Node Reading

        /// <summary>
        /// Returns a copy of the shared memory header
        /// </summary>
        public NodeHeader ReadNodeHeader()
        {
            return (NodeHeader)Marshal.PtrToStructure(new IntPtr(_nodeHeader), typeof(NodeHeader));
        }

        /// <summary>
        /// Attempts to reserve a node from the linked-list for reading with the specified timeout
        /// </summary>
        /// <param name="timeout">The number of milliseconds to wait if a node is not immediately available for reading.</param>
        /// <returns>An unsafe pointer to the node if successful, otherwise null</returns>
        protected virtual Node* GetNodeForReading(int timeout)
        {
            for (; ; )
            {
                int blockIndex = _nodeHeader->ReadStart;
                Node* node = this[blockIndex];
                if (blockIndex == _nodeHeader->WriteEnd)
                {
                    // No data is available, wait for it
                    if (DataExists.WaitOne(timeout))
                        continue;

                    // Timeout
                    return null;
                }

#pragma warning disable 0420 // ignore ref to volatile warning - Interlocked API
                if (Interlocked.CompareExchange(ref _nodeHeader->ReadStart, node->Next, blockIndex) == blockIndex)
                    return node;
#pragma warning restore 0420

                // Another thread has already acquired this node for reading, try again
                continue;
            }
        }

        /// <summary>
        /// Returns a node to the available list of nodes for writing.
        /// </summary>
        /// <param name="node">An unsafe pointer to the node to be returned</param>
        protected virtual void ReturnNode(Node* node)
        {
            // Set the finished reading flag for this node (the node is reserved so no need for locks)
            node->DoneRead = 1;

            // Keep it clean and reset AmountWritten to prepare it for next Write
            node->AmountWritten = 0;

            // Move the read pointer forward as far as possible
            // always starting from ReadEnd to make all contiguous
            // read nodes available for writing.
            for (; ; )
            {
                int blockIndex = _nodeHeader->ReadEnd;
                node = this[blockIndex];
#pragma warning disable 0420 // ignore ref to volatile warning - Interlocked API
                if (Interlocked.CompareExchange(ref node->DoneRead, 0, 1) != 1)
                {
                    // If we get here then another read thread has already moved the pointer
                    // or we have moved ReadEnd as far forward as we can
                    return;
                }

                // Move the pointer forward one node
                Interlocked.CompareExchange(ref _nodeHeader->ReadEnd, node->Next, blockIndex);
#pragma warning restore 0420

               // If a writer thread is waiting on "node available" signal the event
                if (node->Prev == _nodeHeader->WriteStart)
                        NodeAvailable.Set();
            }
        }

        /// <summary>
        /// Reads the next available node for reading into the specified byte array
        /// </summary>
        /// <param name="destination">Reference to the buffer</param>
        /// <param name="startIndex">The index within the buffer to start writing from</param>
        /// <param name="timeout">The maximum number of milliseconds to wait for a node to become available for reading (default 1000ms)</param>
        /// <returns>The number of bytes read</returns>
        /// <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>
        public virtual int Read(byte[] destination, int startIndex = 0, int timeout = 1000)
        {
            Node* node = GetNodeForReading(timeout);
            if (node == null) return 0;

            //int amount = Math.Min(buffer.Length, NodeBufferSize);
            int amount = Math.Min(destination.Length - startIndex, node->AmountWritten);

            // Copy the data
            Marshal.Copy(new IntPtr(BufferStartPtr + node->Offset), destination, startIndex, amount);

            // Return the node for further writing
            ReturnNode(node);

            return amount;
        }

        /// <summary>
        /// Reads the next available node for reading into the specified structure array
        /// </summary>
        /// <typeparam name="T">The structure type to be read</typeparam>
        /// <param name="destination">Reference to the buffer</param>
        /// <param name="startIndex">The index within the destination to start writing to.</param>
        /// <param name="timeout">The maximum number of milliseconds to wait for a node to become available for reading (default 1000ms)</param>
        /// <returns>The number of elements read into destination</returns>
        /// <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>
        public virtual int Read<T>(T[] destination, int startIndex = 0, int timeout = 1000)
            where T : struct
        {
            Node* node = GetNodeForReading(timeout);
            if (node == null) return 0;

            // Copy the data using the FastStructure class (much faster than the MemoryMappedViewAccessor ReadArray<T> method)
            int count = Math.Min(destination.Length - startIndex, node->AmountWritten / FastStructure.SizeOf<T>());
            base.ReadArray<T>(destination, startIndex, count, node->Offset);

            // Return the node for further writing
            ReturnNode(node);

            return count;
        }

        /// <summary>
        /// Reads the next available node for reading into the a structure
        /// </summary>
        /// <typeparam name="T">The structure type to be read</typeparam>
        /// <param name="destination">The resulting structure if successful otherwise default(T)</param>
        /// <param name="timeout">The maximum number of milliseconds to wait for a node to become available for reading (default 1000ms)</param>
        /// <returns>The number of bytes read</returns>
        /// <exception cref="ArgumentOutOfRangeException">If the size of <typeparamref name="T"/> is larger than <see cref="NodeBufferSize"/>.</exception>
        public virtual int Read<T>(out T destination, int timeout = 1000)
            where T: struct
        {
            int structSize = Marshal.SizeOf(typeof(T));
            if (structSize > NodeBufferSize)
                throw new ArgumentOutOfRangeException("T", "The size of structure " + typeof(T).Name + " is larger than NodeBufferSize");

            // Attempt to retrieve a node
            Node* node = GetNodeForReading(timeout);
            if (node == null)
            {
                destination = default(T);
                return 0;
            }

            // Copy the data using the MemoryMappedViewAccessor
            base.Read<T>(out destination, node->Offset);

            // Return the node for further writing
            ReturnNode(node);

            return structSize;
        }

        /// <summary>
        /// Reads the next available node for reading into the specified memory location with the specified length
        /// </summary>
        /// <param name="destination">Pointer to the buffer</param>
        /// <param name="length">The maximum length of <paramref name="destination"/></param>
        /// <param name="timeout">The maximum number of milliseconds to wait for a node to become available for reading (default 1000ms)</param>
        /// <returns>The number of bytes read</returns>
        /// <remarks>The maximum number of bytes that can be read is the minimum of the <paramref name="length"/> and <see cref="Node.AmountWritten"/>.</remarks>
        public virtual int Read(IntPtr destination, int length, int timeout = 1000)
        {
            Node* node = GetNodeForReading(timeout);
            if (node == null) return 0;

            //int amount = Math.Min(length, NodeBufferSize);
            int amount = Math.Min(length, node->AmountWritten);

            // Copy the data
            base.Read(destination, amount, node->Offset);

            // Return node for further writing
            ReturnNode(node);

            return amount;
        }

        /// <summary>
        /// Reserves a node for reading and then calls the provided <paramref name="readFunc"/> to perform the read operation.
        /// </summary>
        /// <param name="readFunc">A function used to read from the node's buffer. The first parameter is a pointer to the node's buffer. 
        /// The provided function should return the number of bytes read.</param>
        /// <param name="timeout">The maximum number of milliseconds to wait for a node to become available for reading (default 1000ms)</param>
        /// <returns>The number of bytes read</returns>
        public virtual int Read(Func<IntPtr, int> readFunc, int timeout = 1000)
        {
            Node* node = GetNodeForReading(timeout);
            if (node == null) return 0;

            int amount = 0;
            try
            {
                // Pass pointer to buffer directly to custom read function
                amount = readFunc(new IntPtr(BufferStartPtr + node->Offset));
            }
            finally
            {
                // Return the node for further writing
                ReturnNode(node);
            }
            return amount;
        }

        #endregion
    }
}


================================================
FILE: SharedMemory/FastStructure.cs
================================================
// SharedMemory (File: SharedMemory\FastStructure.cs)
// Copyright (c) 2014 Justin Stenning
// http://spazzarama.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// The SharedMemory library is inspired by the following Code Project article:
//   "Fast IPC Communication Using Shared Memory and InterlockedCompareExchange"
//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int
using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;

namespace SharedMemory
{
    /// <summary>
    /// Provides fast reading and writing of generic structures to a memory location using IL emitted functions.
    /// </summary>
    public static class FastStructure
    {
        /// <summary>
        /// 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.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="structure"></param>
        /// <returns>A pointer to the provided structure in memory.</returns>
        /// <see cref="FastStructure{T}.GetPtr"/>
        public static unsafe void* GetPtr<T>(ref T structure)
            where T : struct
        {
            return FastStructure<T>.GetPtr(ref structure);
        }

        /// <summary>
        /// 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"/>.
        /// <para>The equivalent non-generic C# code:</para>
        /// <code>
        /// unsafe MyStruct ReadFromPointer(byte* pointer)
        /// {
        ///     return *(MyStruct*)pointer;
        /// }
        /// </code>
        /// </summary>
        /// <typeparam name="T">Any value/structure type</typeparam>
        /// <param name="pointer">Unsafe pointer to memory to load the value from</param>
        /// <returns>The newly loaded value</returns>
        public static unsafe T PtrToStructure<T>(IntPtr pointer)
            where T : struct
        {
            return FastStructure<T>.PtrToStructure(pointer);
        }

        /// <summary>
        /// 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.
        /// <para>There is no exact equivalent possible in C#, the closest possible (generates the same IL) is the following code:</para>
        /// <code>
        /// unsafe void WriteToPointer(ref SharedHeader dest, ref SharedHeader src)
        /// {
        ///     dest = src;
        /// }
        /// </code>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="pointer"></param>
        /// <param name="structure"></param>
        public static unsafe void StructureToPtr<T>(ref T structure, IntPtr pointer)
            where T : struct
        {
            FastStructure<T>.StructureToPtr(ref structure, pointer);
        }

        /// <summary>
        /// Copy bytes of structure into the existing buffer at index
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="structure"></param>
        /// <param name="buffer"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        public static unsafe void CopyTo<T>(ref T structure, byte[] buffer, int startIndex = 0)
            where T : struct
        {
            if (buffer == null)
                throw new ArgumentNullException("buffer");
            if (startIndex > buffer.Length || startIndex < 0)
                throw new ArgumentOutOfRangeException("startIndex");

            fixed (byte* p = &buffer[startIndex])
            {
                StructureToPtr<T>(ref structure, new IntPtr(p));
            }
        }

        /// <summary>
        /// Return a byte[] for the provided structure
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="structure"></param>
        /// <returns></returns>
        public static unsafe byte[] ToBytes<T>(ref T structure)
            where T : struct
        {
            byte[] result = new byte[FastStructure<T>.Size];
            fixed (byte* p = &result[0])
            {
                StructureToPtr<T>(ref structure, new IntPtr(p));
                return result;
            }
        }

        /// <summary>
        /// Read structure from the provided byte array
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="buffer"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        public static unsafe T FromBytes<T>(byte[] buffer, int startIndex = 0)
            where T : struct
        {
            if (buffer == null)
                throw new ArgumentNullException("buffer");
            if (startIndex > buffer.Length || startIndex < 0)
                throw new ArgumentOutOfRangeException("startIndex");

            fixed (byte* p = &buffer[startIndex])
            {
                return PtrToStructure<T>(new IntPtr(p));
            }
        }

        /// <summary>
        /// Retrieve the cached size of a structure
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        /// <remarks>Caches the size by type</remarks>
        /// <see cref="FastStructure{T}.Size"/>
        public static int SizeOf<T>()
            where T : struct
        {
            return FastStructure<T>.Size;
        }

        /// <summary>
        /// Reads a number of elements from a memory location into the provided buffer starting at the specified index.
        /// </summary>
        /// <typeparam name="T">The structure type</typeparam>
        /// <param name="buffer">The destination buffer.</param>
        /// <param name="source">The source memory location.</param>
        /// <param name="index">The start index within <paramref name="buffer"/>.</param>
        /// <param name="count">The number of elements to read.</param>
        public static unsafe void ReadArray<T>(T[] buffer, IntPtr source, int index, int count)
            where T : struct
        {
            uint elementSize = (uint)SizeOf<T>();

            if (buffer == null)
                throw new ArgumentNullException("buffer");
            if (count < 0)
                throw new ArgumentOutOfRangeException("count");
            if (index < 0)
                throw new ArgumentOutOfRangeException("index");
            if (buffer.Length - index < count)
                throw new ArgumentException("Invalid offset into array specified by index and count");

            void* ptr = source.ToPointer();
            byte* p = (byte*)FastStructure.GetPtr<T>(ref buffer[0]);
#if NETCORE
            Buffer.MemoryCopy(ptr, p + (index * elementSize), elementSize * count, elementSize * count);
#else
            UnsafeNativeMethods.CopyMemoryPtr(p + (index * elementSize), ptr, (uint)(elementSize * count));
#endif
        }

        /// <summary>
        /// Reads a number of elements from a memory location into the provided buffer starting at the specified index.
        /// </summary>
        /// <param name="buffer">The destination buffer.</param>
        /// <param name="source">The source memory location.</param>
        /// <param name="index">The start index within <paramref name="buffer"/>.</param>
        /// <param name="count">The number of elements to read.</param>
        public static unsafe void ReadBytes(byte[] buffer, IntPtr source, int index, int count)
        {
            uint elementSize = sizeof(byte);

            if (buffer == null)
                throw new ArgumentNullException("buffer");
            if (count < 0)
                throw new ArgumentOutOfRangeException("count");
            if (index < 0)
                throw new ArgumentOutOfRangeException("index");
            if (buffer.Length - index < count)
                throw new ArgumentException("Invalid offset into array specified by index and count");

            void* ptr = source.ToPointer();

            fixed (byte* p = &buffer[0])
            {
#if NETCORE
                Buffer.MemoryCopy(ptr, p + (index * elementSize), elementSize * count, elementSize * count);
#else
                UnsafeNativeMethods.CopyMemoryPtr(p + (index * elementSize), ptr, (uint)(elementSize * count));
#endif
            }
        }

        /// <summary>
        /// Writes a number of elements to a memory location from the provided buffer starting at the specified index.
        /// </summary>
        /// <typeparam name="T">The structure type</typeparam>
        /// <param name="destination">The destination memory location.</param>
        /// <param name="buffer">The source buffer.</param>
        /// <param name="index">The start index within <paramref name="buffer"/>.</param>
        /// <param name="count">The number of elements to write.</param>
        public static unsafe void WriteArray<T>(IntPtr destination, T[] buffer, int index, int count)
            where T : struct
        {
            uint elementSize = (uint)SizeOf<T>();

            if (buffer == null)
                throw new ArgumentNullException("buffer");
            if (count < 0)
                throw new ArgumentOutOfRangeException("count");
            if (index < 0)
                throw new ArgumentOutOfRangeException("index");
            if (buffer.Length - index < count)
                throw new ArgumentException("Invalid offset into array specified by index and count");

            void* ptr = destination.ToPointer();
            byte* p = (byte*)FastStructure.GetPtr<T>(ref buffer[0]);
#if NETCORE
            Buffer.MemoryCopy(p + (index * elementSize), ptr, elementSize * count, elementSize * count);
#else
            UnsafeNativeMethods.CopyMemoryPtr(ptr, p + (index * elementSize), (uint)(elementSize * count));
#endif
        }

        /// <summary>
        /// Writes a number of elements to a memory location from the provided buffer starting at the specified index.
        /// </summary>
        /// <param name="destination">The destination memory location.</param>
        /// <param name="buffer">The source buffer.</param>
        /// <param name="index">The start index within <paramref name="buffer"/>.</param>
        /// <param name="count">The number of elements to write.</param>
        public static unsafe void WriteBytes(IntPtr destination, byte[] buffer, int index, int count)
        {
            uint elementSize = sizeof(byte);

            if (buffer == null)
                throw new ArgumentNullException("buffer");
            if (count < 0)
                throw new ArgumentOutOfRangeException("count");
            if (index < 0)
                throw new ArgumentOutOfRangeException("index");
            if (buffer.Length - index < count)
                throw new ArgumentException("Invalid offset into array specified by index and count");

            void* ptr = destination.ToPointer();
            fixed (byte* p = &buffer[0])
            {
#if NETCORE
                Buffer.MemoryCopy(p + (index * elementSize), ptr, elementSize * count, elementSize * count);
#else
                UnsafeNativeMethods.CopyMemoryPtr(ptr, p + (index * elementSize), (uint)(elementSize * count));
#endif
            }
        }
    }

    /// <summary>
    /// Emits optimized IL for the reading and writing of structures to/from memory.
    /// <para>For a 32-byte structure with 1 million iterations:</para>
    /// <para>The <see cref="FastStructure{T}.PtrToStructure"/> method performs approx. 20x faster than
    /// <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>
    /// <para>The <see cref="FastStructure{T}.StructureToPtr"/> method performs approx. 8x faster than 
    /// <see cref="System.Runtime.InteropServices.Marshal.StructureToPtr(object, IntPtr, bool)"/> (4ms vs 34ms). </para>
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public static class FastStructure<T>
        where T : struct
    {
        /// <summary>
        /// Delegate that returns a pointer to the provided structure. Use with extreme caution.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public unsafe delegate void* GetPtrDelegate(ref T value);
        
        /// <summary>
        /// Delegate for loading a structure from the specified memory address
        /// </summary>
        /// <param name="pointer"></param>
        /// <returns></returns>
        public delegate T PtrToStructureDelegate(IntPtr pointer);
        
        /// <summary>
        /// Delegate for writing a structure to the specified memory address
        /// </summary>
        /// <param name="value"></param>
        /// <param name="pointer"></param>
        public delegate void StructureToPtrDelegate(ref T value, IntPtr pointer);
        
        /// <summary>
        /// The <see cref="GetPtrDelegate"/> delegate for the generated IL to retrieve a pointer to the structure
        /// </summary>
        public unsafe readonly static GetPtrDelegate GetPtr = BuildFunction();

        /// <summary>
        /// The <see cref="PtrToStructureDelegate"/> delegate for the generated IL to retrieve a structure from a specified memory address.
        /// </summary>
        public readonly static PtrToStructureDelegate PtrToStructure = BuildLoadFromPointerFunction();

        /// <summary>
        /// The <see cref="StructureToPtrDelegate"/> delegate for the generated IL to store a structure at the specified memory address.
        /// </summary>
        public readonly static StructureToPtrDelegate StructureToPtr = BuildWriteToPointerFunction();

        /// <summary>
        /// Cached size of T as determined by <see cref="System.Runtime.InteropServices.Marshal.SizeOf(Type)"/>.
        /// </summary>
        public static readonly int Size = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
        
        private static DynamicMethod method;
        private static DynamicMethod methodLoad;
        private static DynamicMethod methodWrite;

        /// <summary>
        /// Performs once of type compatibility check.
        /// </summary>
        /// <exception cref="ArgumentException">Thrown if the type T is incompatible</exception>
        static FastStructure()
        {
            // Performs compatibility checks upon T
            CheckTypeCompatibility(typeof(T));
        }

        private unsafe static GetPtrDelegate BuildFunction()
        {
            method = new DynamicMethod("GetStructurePtr<" + typeof(T).FullName + ">",
                typeof(void*), new Type[1] { typeof(T).MakeByRefType() }, typeof(FastStructure).Module);

            ILGenerator generator = method.GetILGenerator();
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Conv_U);
            generator.Emit(OpCodes.Ret);
            return (GetPtrDelegate)method.CreateDelegate(typeof(GetPtrDelegate));
        }

        private static unsafe PtrToStructureDelegate BuildLoadFromPointerFunction()
        {
            methodLoad = new DynamicMethod("PtrToStructure<" + typeof(T).FullName + ">",
                typeof(T), new Type[1] { typeof(IntPtr) }, typeof(FastStructure).Module);

            ILGenerator generator = methodLoad.GetILGenerator();
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Ldobj, typeof(T));
            generator.Emit(OpCodes.Ret);

            return (PtrToStructureDelegate)methodLoad.CreateDelegate(typeof(PtrToStructureDelegate));
        }

        private static unsafe StructureToPtrDelegate BuildWriteToPointerFunction()
        {
            methodWrite = new DynamicMethod("StructureToPtr<" + typeof(T).FullName + ">",
                null, new Type[2] { typeof(T).MakeByRefType(), typeof(IntPtr) }, typeof(FastStructure).Module);

            ILGenerator generator = methodWrite.GetILGenerator();
            generator.Emit(OpCodes.Ldarg_1);
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Ldobj, typeof(T));
            generator.Emit(OpCodes.Stobj, typeof(T));
            generator.Emit(OpCodes.Ret);
            return (StructureToPtrDelegate)methodWrite.CreateDelegate(typeof(StructureToPtrDelegate));
        }

        private static void CheckTypeCompatibility(Type t, System.Collections.Generic.HashSet<Type> checkedItems = null)
        {
            if (checkedItems == null)
            {
                checkedItems = new System.Collections.Generic.HashSet<Type>();
                checkedItems.Add(typeof(char));
                checkedItems.Add(typeof(byte));
                checkedItems.Add(typeof(sbyte));
                checkedItems.Add(typeof(bool));
                checkedItems.Add(typeof(double));
                checkedItems.Add(typeof(float));
                checkedItems.Add(typeof(decimal));
                checkedItems.Add(typeof(int));
                checkedItems.Add(typeof(short));
                checkedItems.Add(typeof(long));
                checkedItems.Add(typeof(uint));
                checkedItems.Add(typeof(ushort));
                checkedItems.Add(typeof(ulong));
                checkedItems.Add(typeof(IntPtr));
                checkedItems.Add(typeof(void*));
            }

            if (checkedItems.Contains(t))
                return;
            else
                checkedItems.Add(t);

            FieldInfo[] fi = t.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
            foreach (FieldInfo info in fi)
            {
                if (!info.FieldType.IsPrimitive && !info.FieldType.IsValueType && !info.FieldType.IsPointer)
                {
                    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));
                }

                // Example for adding future marshal attributes as incompatible
                //System.Runtime.InteropServices.MarshalAsAttribute attr;
                //if (TryGetAttribute<System.Runtime.InteropServices.MarshalAsAttribute>(info, out attr))
                //{
                //    if (attr.Value == System.Runtime.InteropServices.UnmanagedType.ByValArray)
                //    {
                //        throw new ArgumentException(String.Format("UnmanagedType.ByValArray is not supported on field {0} in type [{1}].", info.Name, typeof(T).FullName));
                //    }
                //}

                CheckTypeCompatibility(info.FieldType, checkedItems);
            }
        }

        //private static bool TryGetAttribute<T1>(MemberInfo memberInfo, out T1 customAttribute) where T1 : Attribute
        //{
        //    var attributes = memberInfo.GetCustomAttributes(typeof(T1), false).FirstOrDefault();
        //    if (attributes == null)
        //    {
        //        customAttribute = null;
        //        return false;
        //    }
        //    customAttribute = (T1)attributes;
        //    return true;
        //}
    }
}

================================================
FILE: SharedMemory/MemoryMappedFiles/MemoryMappedFile.cs
================================================
// SharedMemory (File: SharedMemory\MemoryMappedFile.cs)
// Copyright (c) 2014 Justin Stenning
// http://spazzarama.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// The SharedMemory library is inspired by the following Code Project article:
//   "Fast IPC Communication Using Shared Memory and InterlockedCompareExchange"
//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.Text;
using Microsoft.Win32.SafeHandles;
using System.Security.Permissions;
using System.Runtime;
using SharedMemory;
using System.Runtime.InteropServices;
using System.Threading;

namespace System.IO.MemoryMappedFiles
{
#if !NET40Plus

    /// <summary>
    /// <para>Very limited .NET 3.5 implementation of a managed wrapper around memory-mapped files to reflect the .NET 4 API.</para>
    /// <para>Only those methods and features necessary for the SharedMemory library have been implemented.</para>
    /// </summary>
#if NETFULL
    [PermissionSet(SecurityAction.LinkDemand)]
#endif
    public sealed class MemoryMappedFile: IDisposable
    {
        SafeMemoryMappedFileHandle _handle;

        /// <summary>
        /// Gets the file handle of a memory-mapped file.
        /// </summary>
        /// <returns>The handle to the memory-mapped file.</returns>
        public SafeMemoryMappedFileHandle SafeMemoryMappedFileHandle
        {
            [SecurityCritical]
            [SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)]
            get
            {
                return this._handle;
            }
        }

        private MemoryMappedFile(SafeMemoryMappedFileHandle handle)
        {
            this._handle = handle;
        }

        /// <summary>
        /// 
        /// </summary>
        ~MemoryMappedFile()
        {
            this.Dispose(false);
        }

        /// <summary>
        /// Creates a new memory-mapped file, throwing an IOException if it already exists
        /// </summary>
        /// <param name="mapName"></param>
        /// <param name="capacity"></param>
        /// <returns></returns>
        public static MemoryMappedFile CreateNew(String mapName, long capacity)
        {
            if (String.IsNullOrEmpty(mapName))
                throw new ArgumentException("mapName cannot be null or empty.");
            if (capacity <= 0)
                throw new ArgumentOutOfRangeException("capacity", "Value must be larger than 0.");
            if (IntPtr.Size == 4 && capacity > ((1024*1024*1024) * (long)4))
                throw new ArgumentOutOfRangeException("capacity", "The capacity cannot be greater than the size of the system's logical address space.");
            return new MemoryMappedFile(DoCreate(mapName, capacity));
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1404:CallGetLastErrorImmediatelyAfterPInvoke"), SecurityCritical]
        [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        private static SafeMemoryMappedFileHandle DoCreate(string mapName, long capacity)
        {
            SafeFileHandle fileHandle = new SafeFileHandle(new IntPtr(-1), true);
            SafeMemoryMappedFileHandle safeHandle = null;

            safeHandle = UnsafeNativeMethods.CreateFileMapping(fileHandle, (UnsafeNativeMethods.FileMapProtection)MemoryMappedFileAccess.ReadWrite, capacity, mapName);
            var lastWin32Error = Marshal.GetLastWin32Error();
            if (!safeHandle.IsInvalid && (lastWin32Error == UnsafeNativeMethods.ERROR_ALREADY_EXISTS))
            {
                throw new System.IO.IOException(UnsafeNativeMethods.GetMessage(lastWin32Error));
            }
            else if (safeHandle.IsInvalid && lastWin32Error > 0)
            {
                throw new System.IO.IOException(UnsafeNativeMethods.GetMessage(lastWin32Error));
            }

            if (safeHandle == null || safeHandle.IsInvalid)
                throw new InvalidOperationException("Cannot create file mapping");

            return safeHandle;
        }

        /// <summary>
        /// Creates a new view accessor
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="size"></param>
        /// <param name="access"></param>
        /// <returns></returns>
        [SecurityCritical]
        [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        public MemoryMappedViewAccessor CreateViewAccessor(long offset, long size, MemoryMappedFileAccess access = MemoryMappedFileAccess.ReadWrite)
        {
            if (offset < 0)
                throw new ArgumentOutOfRangeException("offset", "Value must be non-negative");
            if (size < 0)
                throw new ArgumentOutOfRangeException("size", "Value must be positive or zero for default size");
            if (IntPtr.Size == 4 && size > ((1024 * 1024 * 1024) * (long)4))
                throw new ArgumentOutOfRangeException("size", "The capacity cannot be greater than the size of the system's logical address space.");
            MemoryMappedView memoryMappedView = MemoryMappedView.CreateView(this._handle, access, offset, size);
            return new MemoryMappedViewAccessor(memoryMappedView);
        }

        /// <summary>
        /// Dispose pattern
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        void Dispose(bool disposeManagedResources)
        {
            if (this._handle != null && !this._handle.IsClosed)
            {
                this._handle.Dispose();
                this._handle = null;
            }
        }

        /// <summary>
        /// Opens an existing memory-mapped file. Throws FileNotFoundException if it doesn't exist.
        /// </summary>
        /// <param name="mapName"></param>
        /// <returns></returns>
        public static MemoryMappedFile OpenExisting(string mapName)
        {
            SafeMemoryMappedFileHandle safeMemoryMappedFileHandle = UnsafeNativeMethods.OpenFileMapping((uint)MemoryMappedFileRights.ReadWrite, false, mapName);
            int lastWin32Error = Marshal.GetLastWin32Error();
            if (safeMemoryMappedFileHandle.IsInvalid)
            {
                if (lastWin32Error == UnsafeNativeMethods.ERROR_FILE_NOT_FOUND)
                    throw new FileNotFoundException();
                throw new System.IO.IOException(UnsafeNativeMethods.GetMessage(lastWin32Error));
            }
            return new MemoryMappedFile(safeMemoryMappedFileHandle);
        }
    }

#endif
}


================================================
FILE: SharedMemory/MemoryMappedFiles/MemoryMappedFileAccess.cs
================================================
// SharedMemory (File: SharedMemory\MemoryMappedFileAccess.cs)
// Copyright (c) 2014 Justin Stenning
// http://spazzarama.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// The SharedMemory library is inspired by the following Code Project article:
//   "Fast IPC Communication Using Shared Memory and InterlockedCompareExchange"
//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int
using SharedMemory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace System.IO.MemoryMappedFiles
{
#if !NET40Plus
    /// <summary>
    /// Used when creating a memory mapped file
    /// </summary>
    public enum MemoryMappedFileAccess: uint
    {
        /// <summary>
        /// Read
        /// </summary>
        Read = 2,
        /// <summary>
        /// Read/Write
        /// </summary>
        ReadWrite = 4,
        /// <summary>
        /// CopyOnWrite
        /// </summary>
        CopyOnWrite = 8,
        /// <summary>
        /// Read Execute
        /// </summary>
        ReadExecute = 32,
        /// <summary>
        /// Read/Write Execute
        /// </summary>
        ReadWriteExecute = 64
    }

    internal static class MemoryMappedFileAccessExtensions
    {
        internal static UnsafeNativeMethods.FileMapAccess ToMapViewFileAccess(this MemoryMappedFileAccess access)
        {
            switch (access)
            {
                case MemoryMappedFileAccess.Read:
                    return UnsafeNativeMethods.FileMapAccess.FileMapRead;
                case MemoryMappedFileAccess.ReadWrite:
                    return UnsafeNativeMethods.FileMapAccess.FileMapRead | UnsafeNativeMethods.FileMapAccess.FileMapWrite;
                case MemoryMappedFileAccess.ReadExecute:
                    return UnsafeNativeMethods.FileMapAccess.FileMapRead | UnsafeNativeMethods.FileMapAccess.FileMapExecute;
                case MemoryMappedFileAccess.ReadWriteExecute:
                    return UnsafeNativeMethods.FileMapAccess.FileMapRead | UnsafeNativeMethods.FileMapAccess.FileMapWrite | UnsafeNativeMethods.FileMapAccess.FileMapExecute;
                default:
                    return UnsafeNativeMethods.FileMapAccess.FileMapAllAccess;
            }
        }
    }
#endif
}


================================================
FILE: SharedMemory/MemoryMappedFiles/MemoryMappedFileRights.cs
================================================
// SharedMemory (File: SharedMemory\MemoryMappedFileRights.cs)
// Copyright (c) 2014 Justin Stenning
// http://spazzarama.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// The SharedMemory library is inspired by the following Code Project article:
//   "Fast IPC Communication Using Shared Memory and InterlockedCompareExchange"
//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace System.IO.MemoryMappedFiles
{
#if !NET40Plus
    /// <summary>
    /// Used for opening a memory-mapped file
    /// </summary>
    [Flags]
    public enum MemoryMappedFileRights: uint
    {
        /// <summary>The right to add data to a file or remove data from a file.</summary>
        Write = 0x02,
        /// <summary>The right to open and copy a file as read-only.</summary>
        Read = 0x04,
        /// <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>
        ReadWrite = MemoryMappedFileRights.Write | MemoryMappedFileRights.Read,
    }
#endif
}


================================================
FILE: SharedMemory/MemoryMappedFiles/MemoryMappedView.cs
================================================
// SharedMemory (File: SharedMemory\MemoryMappedView.cs)
// Copyright (c) 2014 Justin Stenning
// http://spazzarama.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// The SharedMemory library is inspired by the following Code Project article:
//   "Fast IPC Communication Using Shared Memory and InterlockedCompareExchange"
//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int
using Microsoft.Win32.SafeHandles;
using SharedMemory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text;

namespace System.IO.MemoryMappedFiles
{
#if !NET40Plus
    /// <summary>
    /// <para>Very limited .NET 3.5 implementation of a managed wrapper around memory-mapped files to reflect the .NET 4 API.</para>
    /// <para>Only those methods and features necessary for the SharedMemory library have been implemented.</para>
    /// </summary>
#if NETFULL
    [PermissionSet(SecurityAction.LinkDemand)]
#endif
    public sealed class MemoryMappedView : IDisposable
    {
        SafeMemoryMappedViewHandle _handle;
        
        /// <summary>
        /// 
        /// </summary>
        public SafeMemoryMappedViewHandle SafeMemoryMappedViewHandle
        {
            get { return this._handle; }
        }

        long _size;
        long _offset;

        /// <summary>
        /// The size of the view (from offset to end)
        /// </summary>
        public long Size { get { return _size; } }
        
        /// <summary>
        /// The start of the view (the handle itself will be aligned based on the allocation granularity)
        /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa366548(v=vs.85).aspx
        /// </summary>
        public long ViewStartOffset { get { return _offset; } }

        private MemoryMappedView(SafeMemoryMappedViewHandle handle, long offset, long size)
        {
            this._handle = handle;
            this._offset = offset;
            this._size = size;
        }

        /// <summary>
        /// 
        /// </summary>
        ~MemoryMappedView()
        {
            Dispose(false);
        }

        /// <summary>
        /// Dispose pattern
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        void Dispose(bool disposeManagedResources)
        {
            if (this._handle != null && !this._handle.IsClosed)
                this._handle.Dispose();
            this._handle = null;
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1404:CallGetLastErrorImmediatelyAfterPInvoke")]
        internal static MemoryMappedView CreateView(SafeMemoryMappedFileHandle safeMemoryMappedFileHandle, MemoryMappedFileAccess access, long offset, long size)
        {
            // http://msdn.microsoft.com/en-us/library/windows/desktop/aa366548(v=vs.85).aspx
            UnsafeNativeMethods.SYSTEM_INFO info = new UnsafeNativeMethods.SYSTEM_INFO();
            UnsafeNativeMethods.GetSystemInfo(ref info);

            // To calculate where to start the file mapping, round down the
            // offset of the data into the memory-mapped file to the nearest multiple of the
            // system allocation granularity.
            long fileMapStart = (offset / info.dwAllocationGranularity) * info.dwAllocationGranularity;
            // How large will the file mapping object be?
            long mapViewSize = (offset % info.dwAllocationGranularity) + size;
            // The data of interest is not necessarily at the beginning of the
            // view, so determine how far into the view to set the pointer.
            long viewDelta = offset - fileMapStart;

            SafeMemoryMappedViewHandle safeHandle = UnsafeNativeMethods.MapViewOfFile(safeMemoryMappedFileHandle, access.ToMapViewFileAccess(), (ulong)fileMapStart, new UIntPtr((ulong)mapViewSize));
            var lastWin32Error = Marshal.GetLastWin32Error();
            if (safeHandle.IsInvalid)
            {
                if (lastWin32Error == UnsafeNativeMethods.ERROR_FILE_NOT_FOUND)
                    throw new FileNotFoundException();
                throw new System.IO.IOException(UnsafeNativeMethods.GetMessage(lastWin32Error));
            }

            return new MemoryMappedView(safeHandle, viewDelta, size);
        }
    }
#endif
}


================================================
FILE: SharedMemory/MemoryMappedFiles/MemoryMappedViewAccessor.cs
================================================
// SharedMemory (File: SharedMemory\MemoryMappedViewAccessor.cs)
// Copyright (c) 2014 Justin Stenning
// http://spazzarama.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// The SharedMemory library is inspired by the following Code Project article:
//   "Fast IPC Communication Using Shared Memory and InterlockedCompareExchange"
//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int
using Microsoft.Win32.SafeHandles;
using SharedMemory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Text;

namespace System.IO.MemoryMappedFiles
{
#if !NET40Plus
    /// <summary>
    /// 
    /// </summary>
#if NETFULL
    [PermissionSet(SecurityAction.LinkDemand)]
#endif
    public sealed class MemoryMappedViewAccessor : IDisposable
    {
        MemoryMappedView _view;

        internal MemoryMappedViewAccessor(MemoryMappedView memoryMappedView)
        {
            this._view = memoryMappedView;
        }
        
        /// <summary>
        /// 
        /// </summary>
        public SafeMemoryMappedViewHandle SafeMemoryMappedViewHandle
        {
            [SecurityCritical]
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            get
            {
                return this._view.SafeMemoryMappedViewHandle;
            }
        }

        /// <summary>
        /// Dispose pattern
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposeManagedResources)
        {
            if (_view != null)
                _view.Dispose();
            _view = null;
        }

        internal static unsafe void PtrToStructure<T>(byte* ptr, out T structure)
            where T : struct
        {
            structure = FastStructure.PtrToStructure<T>((IntPtr)ptr);
            //var tr = __makeref(structure);
            //*(IntPtr*)&tr = (IntPtr)ptr;
            //structure = __refvalue( tr,T);
        }

        internal static unsafe void StructureToPtr<T>(ref T structure, byte* ptr)
            where T : struct
        {
            FastStructure.StructureToPtr<T>(ref structure, (IntPtr)ptr);
        }

        internal unsafe void Write<T>(long position, ref T structure)
            where T: struct
        {
            uint elementSize = (uint)Marshal.SizeOf(typeof(T));
            if (position > this._view.Size - elementSize)
                throw new ArgumentOutOfRangeException("position", "");

            try
            {
                byte* ptr = null;
                _view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);
                ptr += +_view.ViewStartOffset + position;
                StructureToPtr(ref structure, ptr);
            }
            finally
            {
                _view.SafeMemoryMappedViewHandle.ReleasePointer();
            }
        }

        internal unsafe void WriteArray<T>(long position, T[] buffer, int index, int count)
            where T : struct
        {
            uint elementSize = (uint)Marshal.SizeOf(typeof(T));

            if (position > this._view.Size - (elementSize * count))
                throw new ArgumentOutOfRangeException("position");
            
            try
            {
                byte* ptr = null;
                _view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);
                ptr += _view.ViewStartOffset + position;

                FastStructure.WriteArray<T>((IntPtr)ptr, buffer, index, count);

                //for (var i = 0; i < count; i++)
                //{
                //    StructureToPtr(ref buffer[index + i], ptr + (i * elementSize));
                //}
            }
            finally
            {
                _view.SafeMemoryMappedViewHandle.ReleasePointer();
            }
        }

        internal unsafe void Read<T>(long position, out T structure)
            where T: struct
        {
            uint size = (uint)Marshal.SizeOf(typeof(T));
            if (position > this._view.Size - size)
                throw new ArgumentOutOfRangeException("position", "");
            try
            {
                byte* ptr = null;
                _view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);
                ptr += _view.ViewStartOffset + position;
                PtrToStructure(ptr, out structure);
            }
            finally
            {
                _view.SafeMemoryMappedViewHandle.ReleasePointer();
            }
        }

        internal unsafe void ReadArray<T>(long position, T[] buffer, int index, int count)
            where T : struct
        {
            uint elementSize = (uint)FastStructure.SizeOf<T>();

            if (buffer == null)
                throw new ArgumentNullException("buffer");
            if (position > this._view.Size - (elementSize * count))
                throw new ArgumentOutOfRangeException("position");
            try
            {
                byte* ptr = null;
                _view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);
                ptr += _view.ViewStartOffset + position;

                FastStructure.ReadArray<T>(buffer, (IntPtr)ptr, index, count);
                
                //for (var i = 0; i < count; i++)
                //{
                //    PtrToStructure(ptr + (i * elementSize), out buffer[index + i]);
                //}
            }
            finally
            {
                _view.SafeMemoryMappedViewHandle.ReleasePointer();
            }
        }
    }
#endif
}


================================================
FILE: SharedMemory/MemoryMappedFiles/SafeMemoryMappedFileHandle.cs
================================================
// SharedMemory (File: SharedMemory\safememorymappedfilehandle.cs)
// Copyright (c) 2014 Justin Stenning
// http://spazzarama.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// The SharedMemory library is inspired by the following Code Project article:
//   "Fast IPC Communication Using Shared Memory and InterlockedCompareExchange"
//   http://www.codeproject.com/Articles/14740/Fast-IPC-Communication-Using-Shared-Memory-and-Int
using SharedMemory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Permissions;
using System.Text;

namespace Microsoft.Win32.SafeHandles
{
#if !NET40Plus
    /// <summary>
    /// Provides a safe handle that represents a memory-mapped file for sequential access.
    /// </summary>
    public sealed class SafeMemoryMappedFileHandle: SafeHandleZeroOrMinusOneIsInvalid
    {
        [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
        internal SafeMemoryMappedFileHandle()
            : base(true)
        {
        }

        [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
        internal SafeMemoryMappedFileHandle(IntPtr handle, bool ownsHandle)
            : base(ownsHandle)
        {
            base.SetHandle(handle);
        }

        /// <summary>
        /// Closes the memory-mapped file handle
        /// </summary>
        /// <returns></returns>
        protected override bool ReleaseHandle()
        {
            try
            {
                return UnsafeNativeMethods.CloseHandle(this.handle);
            }
            finally
            {
                this.handle = IntPtr.Zero;
            }
        }
    }
#endif
}


================================================
FILE: SharedMemory/MemoryMappedFiles/SafeMemoryMappedViewHandle.cs
================================================
// SharedMemory (File: SharedMemory\safememorymappedviewhandle.cs)
// Copyright (c) 2014 Justin Stenning
// http://spazzarama.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software 
Download .txt
gitextract_lcci0mpz/

├── .gitignore
├── Examples/
│   ├── ClientTest/
│   │   ├── ClientTest.csproj
│   │   └── Program.cs
│   ├── RpcTest/
│   │   ├── Program.cs
│   │   └── RpcTest.csproj
│   ├── ServerTest/
│   │   ├── Program.cs
│   │   └── ServerTest.csproj
│   └── SingleProcess/
│       ├── CommandLineParser.cs
│       ├── Program.cs
│       └── SingleProcess.csproj
├── LICENSE.md
├── README.md
├── SharedMemory/
│   ├── BufferReadWrite.cs
│   ├── BufferWithLocks.cs
│   ├── CircularBuffer.cs
│   ├── FastStructure.cs
│   ├── MemoryMappedFiles/
│   │   ├── MemoryMappedFile.cs
│   │   ├── MemoryMappedFileAccess.cs
│   │   ├── MemoryMappedFileRights.cs
│   │   ├── MemoryMappedView.cs
│   │   ├── MemoryMappedViewAccessor.cs
│   │   ├── SafeMemoryMappedFileHandle.cs
│   │   └── SafeMemoryMappedViewHandle.cs
│   ├── RpcBuffer.cs
│   ├── SharedArray.cs
│   ├── SharedBuffer.cs
│   ├── SharedHeader.cs
│   ├── SharedMemory.csproj
│   ├── SharedMemory.licenseheader
│   ├── UnsafeNativeMethods.cs
│   └── Utilities/
│       ├── ArraySlice.cs
│       └── ExpandingArray.cs
├── SharedMemory.Tests/
│   ├── ArraySliceTests.cs
│   ├── ArrayTests.cs
│   ├── BufferReadWriteTests.cs
│   ├── CircularBufferTests.cs
│   ├── ExpandingArrayTests.cs
│   ├── FastStructureTests.cs
│   ├── RpcBufferTests.cs
│   └── SharedMemory.Tests.csproj
├── SharedMemory.nuspec
├── SharedMemory.sln
├── appveyor-develop.yml
└── appveyor.yml
Download .txt
SYMBOL INDEX (396 symbols across 30 files)

FILE: Examples/ClientTest/Program.cs
  class Program (line 41) | class Program
    method Main (line 43) | static void Main(string[] args)

FILE: Examples/RpcTest/Program.cs
  class Program (line 34) | class Program
    method Main (line 36) | static void Main(string[] args)

FILE: Examples/ServerTest/Program.cs
  class Program (line 39) | class Program
    method Main (line 41) | static void Main(string[] args)

FILE: Examples/SingleProcess/CommandLineParser.cs
  type ArgumentType (line 220) | [Flags]
  class ArgumentAttribute (line 265) | [AttributeUsage(AttributeTargets.Field)]
    method ArgumentAttribute (line 272) | public ArgumentAttribute(ArgumentType type)
  class DefaultArgumentAttribute (line 361) | [AttributeUsage(AttributeTargets.Field)]
    method DefaultArgumentAttribute (line 368) | public DefaultArgumentAttribute(ArgumentType type)
  class Parser (line 403) | public sealed class Parser
    method Parser (line 413) | private Parser() { }
    method ParseArgumentsWithUsage (line 424) | public static bool ParseArgumentsWithUsage(string [] arguments, object...
    method ParseArguments (line 444) | public static bool ParseArguments(string [] arguments, object destinat...
    method ParseArguments (line 457) | public static bool ParseArguments(string[] arguments, object destinati...
    method NullErrorReporter (line 463) | private static void NullErrorReporter(string message)
    class HelpArgument (line 467) | private class HelpArgument
    method ParseHelp (line 478) | public static bool ParseHelp(string[] args)
    method ArgumentsUsage (line 494) | public static string ArgumentsUsage(Type argumentType)
    method ArgumentsUsage (line 509) | public static string ArgumentsUsage(Type argumentType, int columns)
    type COORD (line 516) | private struct COORD
    type SMALL_RECT (line 522) | private struct SMALL_RECT
    type CONSOLE_SCREEN_BUFFER_INFO (line 530) | private struct CONSOLE_SCREEN_BUFFER_INFO
    method GetStdHandle (line 539) | [DllImport("kernel32.dll", EntryPoint="GetStdHandle", SetLastError=tru...
    method GetConsoleScreenBufferInfo (line 542) | [DllImport("kernel32.dll", EntryPoint="GetConsoleScreenBufferInfo", Se...
    method GetConsoleWindowWidth (line 549) | public static int GetConsoleWindowWidth()
    method IndexOf (line 567) | public static int IndexOf(StringBuilder text, char value, int startIndex)
    method LastIndexOf (line 585) | public static int LastIndexOf(StringBuilder text, char value, int star...
    method Parser (line 603) | public Parser(Type argumentSpecification, ErrorReporter reporter)
    method GetAttribute (line 658) | private static ArgumentAttribute GetAttribute(FieldInfo field)
    method ReportUnrecognizedArgument (line 668) | private void ReportUnrecognizedArgument(string argument)
    method ParseArgumentList (line 679) | private bool ParseArgumentList(string[] args, object destination)
    method Parse (line 749) | public bool Parse(string[] args, object destination)
    type ArgumentHelpStrings (line 766) | private struct ArgumentHelpStrings
      method ArgumentHelpStrings (line 768) | public ArgumentHelpStrings(string syntax, string help)
    method GetUsageString (line 781) | public string GetUsageString(int screenWidth)
    method AddNewLine (line 866) | private static void AddNewLine(string newLine, StringBuilder builder, ...
    method GetAllHelpStrings (line 871) | private ArgumentHelpStrings[] GetAllHelpStrings()
    method GetHelpStrings (line 888) | private static ArgumentHelpStrings GetHelpStrings(Argument arg)
    method NumberOfParametersToDisplay (line 893) | private int NumberOfParametersToDisplay()
    method LexFileArguments (line 910) | private bool LexFileArguments(string fileName, out string[] arguments)
    method LongName (line 1019) | private static string LongName(ArgumentAttribute attribute, FieldInfo ...
    method ShortName (line 1024) | private static string ShortName(ArgumentAttribute attribute, FieldInfo...
    method HelpText (line 1033) | private static string HelpText(ArgumentAttribute attribute, FieldInfo ...
    method HasHelpText (line 1041) | private static bool HasHelpText(ArgumentAttribute attribute)
    method ExplicitShortName (line 1046) | private static bool ExplicitShortName(ArgumentAttribute attribute)
    method DefaultValue (line 1051) | private static object DefaultValue(ArgumentAttribute attribute, FieldI...
    method ElementType (line 1056) | private static Type ElementType(FieldInfo field)
    method Flags (line 1064) | private static ArgumentType Flags(ArgumentAttribute attribute, FieldIn...
    method IsCollectionType (line 1074) | private static bool IsCollectionType(Type type)
    method IsValidElementType (line 1079) | private static bool IsValidElementType(Type type)
    class Argument (line 1089) | [System.Diagnostics.DebuggerDisplay("Name = {LongName}")]
      method Argument (line 1092) | public Argument(ArgumentAttribute attribute, FieldInfo field, ErrorR...
      method Finish (line 1124) | public bool Finish(object destination)
      method ReportMissingRequiredArgument (line 1144) | private bool ReportMissingRequiredArgument()
      method ReportDuplicateArgumentValue (line 1157) | private void ReportDuplicateArgumentValue(string value)
      method SetValue (line 1162) | public bool SetValue(string value, object destination)
      method ReportBadArgumentValue (line 1199) | private void ReportBadArgumentValue(string value)
      method ParseValue (line 1204) | private bool ParseValue(Type type, string stringData, out object value)
      method AppendValue (line 1271) | private void AppendValue(StringBuilder builder, object value)
      method ClearShortName (line 1316) | public void ClearShortName()

FILE: Examples/SingleProcess/Program.cs
  class Program (line 41) | class Program
    class AppArguments (line 43) | class AppArguments
    method Main (line 57) | static void Main(string[] args)

FILE: SharedMemory.Tests/ArraySliceTests.cs
  class ArraySliceTests (line 35) | [TestClass]
    method ArraySlice_WorksLikeArray (line 38) | [TestMethod]
    method ArraySlice_TestSlice (line 74) | [TestMethod]
    method ApproximatelyEqual (line 113) | public static bool ApproximatelyEqual(double x, double y)

FILE: SharedMemory.Tests/ArrayTests.cs
  class ArrayTests (line 37) | [TestClass]
    method Indexer_ReadWriteInteger_DataMatches (line 40) | [TestMethod]
    method Indexer_OutOfRange_ThrowsException (line 86) | [TestMethod]
    type MyTestStruct (line 144) | [StructLayout(LayoutKind.Sequential)]
    method Test_MyTestStruct (line 180) | [TestMethod]
    method Indexer_ReadWriteComplexStruct_DataMatches (line 189) | [TestMethod]
    method CopyTo_NullArray_ThrowsException (line 209) | [TestMethod]
    method Write_NullArray_ThrowsException (line 228) | [TestMethod]
    method GetEnumerator_IterateItems_DataMatches (line 247) | [TestMethod]
    method AcquireWriteLock_ReadWrite_LocksCorrectly (line 268) | [TestMethod]
    method AcquireReadWriteLocks_ReadWrite_Blocks (line 331) | [TestMethod]
    method IList_Contains (line 357) | [TestMethod]
    method IList_IndexOf (line 373) | [TestMethod]
    method IList_IsReadOnly (line 389) | [TestMethod]

FILE: SharedMemory.Tests/BufferReadWriteTests.cs
  class BufferReadWriteTests (line 6) | [TestClass]
    method Constructor_ProducerConsumer_Created (line 9) | [TestMethod]
    method ReadWrite_Bytes_DataMatches (line 20) | [TestMethod]
    method ReadWrite_TimeoutException (line 42) | [TestMethod]

FILE: SharedMemory.Tests/CircularBufferTests.cs
  class CircularBufferTests (line 37) | [TestClass]
    method Constructor_ProducerEmptyName_ExceptionThrown (line 42) | [TestMethod]
    method Constructor_ProducerNodeCount1_ExceptionThrown (line 82) | [TestMethod]
    method Constructor_ProducerNodeCount0_ExceptionThrown (line 101) | [TestMethod]
    method Constructor_Producer_True (line 124) | [TestMethod]
    method Constructor_ConsumerWithoutProducer_FileNotFoundException (line 133) | [TestMethod]
    method Constructor_DuplicateProducer_IOException (line 150) | [TestMethod]
    method Constructor_ProducerAndConsumer_True (line 168) | [TestMethod]
    method Close_CheckShuttingDown_True (line 179) | [TestMethod]
    method Constructor_BufferTooLarge_ArgumentOutOfRangeException (line 192) | [TestMethod]
    method StructSize_SharedMemoryHeader_Is16bytes (line 225) | [TestMethod]
    method StructSize_Node_Is32bytes (line 231) | [TestMethod]
    method StructSize_SharedMemoryNodeHeader_Is24bytes (line 237) | [TestMethod]
    method ReadWrite_SingleNode_DataMatches (line 247) | [TestMethod]
    method ReadWrite_SingleNode_HeaderIndexesCorrect (line 278) | [TestMethod]
    type MyTestStruct (line 328) | [StructLayout(LayoutKind.Sequential)]
    method ReadWrite_MyTestStruct_DataMatches (line 337) | [TestMethod]
    method ReadWrite_1000NodesIn2NodeRing_DataMatches (line 370) | [TestMethod]
    method WriteMultiple (line 402) | private long WriteMultiple<T>(CircularBuffer smr, T[][] data, out int ...
    method ReadMultiple (line 426) | private long ReadMultiple<T>(CircularBuffer smr, T[][] writtenData, ou...
    method ReadMultipleWithCheck (line 451) | private long ReadMultipleWithCheck<T>(CircularBuffer smr, T[][] writte...
    method ReadWriteAsync_1000NodesIn2NodeRing_DataMatches (line 478) | [TestMethod]
    method ReadWriteAsync_SlowReaderSmallWriterTimeout_DataMatches (line 522) | [TestMethod]
    method ReadWriteAsync_SlowWriterSmallReaderTimeout_DataMatches (line 567) | [TestMethod]
    method ReadWrite_NonSequentialReadWrite_HeaderIndexesCorrect (line 620) | [TestMethod]
    type TestStruct (line 719) | [StructLayout(LayoutKind.Sequential)]
    method ReadWrite_StructuredData_DataMatches (line 726) | [TestMethod]
    method ReadWrite_StructuredData_ReadWriteStartIndex (line 755) | [TestMethod]
    method ReadWrite_IntPtr_DataMatches (line 796) | [TestMethod]
    method ReadWrite_DelegateIntPtr_DataMatches (line 834) | [TestMethod]

FILE: SharedMemory.Tests/ExpandingArrayTests.cs
  class ExpandingArrayTests (line 9) | [TestClass]
    method ExpandingArrayTests_GrownReport (line 12) | [TestMethod]
    method ExpandingArrayTests_Basic (line 58) | [TestMethod]
    method TestEArray (line 67) | private static void TestEArray(ExpandingArray<int> ea)

FILE: SharedMemory.Tests/FastStructureTests.cs
  class FastStructureTests (line 34) | [TestClass]
    type CompatibleStructure (line 39) | [StructLayout(LayoutKind.Sequential, Pack = 1)]
    type IncompatibleNestedStructure (line 57) | [StructLayout(LayoutKind.Sequential)]
    type IncompatibleNestedStructure2 (line 65) | [StructLayout(LayoutKind.Sequential)]
    type HasIncompatibleStructure (line 76) | public struct HasIncompatibleStructure
    type ComplexStructure (line 83) | [StructLayout(LayoutKind.Sequential)]
    method FastStructure_IncompabitibleNestedType (line 96) | [TestMethod]
    method FastStructure_IncompatibleStructure (line 111) | [TestMethod]
    method FastStructure_CompatibleStructureSize (line 126) | [TestMethod]
    method FastStructure_ComplexStructureSize (line 132) | [TestMethod]
    method FastStructure_AllocHGlobalReadWrite (line 140) | [TestMethod]

FILE: SharedMemory.Tests/RpcBufferTests.cs
  class RpcBufferTests (line 35) | [TestClass]
    method Initialise (line 42) | [TestInitialize]
    method Cleanup (line 48) | [TestCleanup]
    method Constructor_MasterSlave_Create (line 55) | [TestMethod]
    method Constructor_BufferCapacityOutOfRange (line 66) | [TestMethod]
    method RPC_MasterCallsSlave (line 73) | [TestMethod]
    method RPC_MasterCallsSlave_Async_WithCancellationToken_WithActualCancellation (line 91) | [TestMethod]
    method RPC_MasterCallsSlave_Async_WithCancellationToken_WithoutCancellation (line 120) | [TestMethod]
    method RPC_MasterCallsSlave_Sync_WithCancellationToken_WithActualCancellation (line 142) | [TestMethod]
    method RPC_MasterCallsSlave_Sync_WithCancellationToken_WithoutCancellation (line 171) | [TestMethod]
    method RPC_Statistics_Reset (line 193) | [TestMethod]
    method RPC_MasterCallsSlave_Exception (line 223) | [TestMethod]
    method RPC_Bidirectional_Nested (line 237) | [TestMethod]
    method RPC_Timeout (line 257) | [TestMethod]
    method RPC_Timeout_FireAndForget (line 274) | [TestMethod]
    method RPC_LoadTest_5k_Small (line 292) | [TestMethod]
    method RPC_LoadTest_5k_Small_Multi_Thread (line 317) | [TestMethod]
    method RPC_LoadTest_1k_Large (line 356) | [TestMethod]
    method RPC_LoadTest_NestedCalls (line 385) | [TestMethod]
    method RPC_SlaveCallsMasterAfterClosed_Exception (line 413) | [TestMethod]
    method RPC_Dispose (line 433) | [TestMethod]

FILE: SharedMemory/BufferReadWrite.cs
  class BufferReadWrite (line 39) | #if NETFULL
    method BufferReadWrite (line 51) | public BufferReadWrite(string name, int bufferSize)
    method BufferReadWrite (line 61) | public BufferReadWrite(string name)
    method Write (line 77) | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "...
    method Write (line 90) | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "...
    method Write (line 103) | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "...
    method Write (line 114) | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "...
    method Read (line 130) | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "...
    method Read (line 143) | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "...
    method Read (line 156) | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "...
    method Read (line 167) | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "...

FILE: SharedMemory/BufferWithLocks.cs
  class BufferWithLocks (line 47) | [PermissionSet(SecurityAction.LinkDemand)]
    method BufferWithLocks (line 70) | protected BufferWithLocks(string name, long bufferSize, bool ownsShare...
    method AcquireReadLock (line 106) | public bool AcquireReadLock(int millisecondsTimeout = System.Threading...
    method ReleaseReadLock (line 117) | public void ReleaseReadLock()
    method AcquireWriteLock (line 129) | public bool AcquireWriteLock(int millisecondsTimeout = System.Threadin...
    method ReleaseWriteLock (line 140) | public void ReleaseWriteLock()
    method WriteWait (line 152) | private void WriteWait()
    method Write (line 164) | protected override void Write<T>(ref T data, long bufferPosition = 0)
    method Write (line 176) | protected override void Write<T>(T[] buffer, long bufferPosition = 0)
    method Write (line 188) | protected override void Write(IntPtr ptr, int length, long bufferPosit...
    method Write (line 199) | protected override void Write(Action<IntPtr> writeFunc, long bufferPos...
    method ReadWait (line 212) | private void ReadWait()
    method Read (line 224) | protected override void Read<T>(out T data, long bufferPosition = 0)
    method Read (line 236) | protected override void Read<T>(T[] buffer, long bufferPosition = 0)
    method Read (line 248) | protected override void Read(IntPtr destination, int length, long buff...
    method Read (line 259) | protected override void Read(Action<IntPtr> readFunc, long bufferPosit...
    method Dispose (line 273) | protected override void Dispose(bool disposeManagedResources)

FILE: SharedMemory/CircularBuffer.cs
  class CircularBuffer (line 42) | [PermissionSet(SecurityAction.LinkDemand)]
    type NodeHeader (line 132) | [StructLayout(LayoutKind.Sequential)]
    type Node (line 168) | [StructLayout(LayoutKind.Sequential)]
    method CircularBuffer (line 228) | public CircularBuffer(string name, int nodeCount, int nodeBufferSize)
    method CircularBuffer (line 238) | public CircularBuffer(string name)
    method CircularBuffer (line 244) | private CircularBuffer(string name, int nodeCount, int nodeBufferSize,...
    method DoOpen (line 271) | protected override bool DoOpen()
    method InitialiseNodeHeader (line 302) | private void InitialiseNodeHeader()
    method InitialiseLinkedListNodes (line 320) | private void InitialiseLinkedListNodes()
    method DoClose (line 355) | protected override void DoClose()
    method GetNodeForWriting (line 377) | protected virtual Node* GetNodeForWriting(int timeout)
    method PostNode (line 407) | protected virtual void PostNode(Node* node)
    method Write (line 446) | public virtual int Write(byte[] source, int startIndex = 0, int timeou...
    method Write (line 473) | public virtual int Write<T>(T[] source, int startIndex = 0, int timeou...
    method Write (line 499) | public virtual int Write<T>(ref T source, int timeout = 1000)
    method Write (line 528) | public virtual int Write(IntPtr source, int length, int timeout = 1000)
    method Write (line 552) | public virtual int Write(Func<IntPtr, int> writeFunc, int timeout = 1000)
    method ReadNodeHeader (line 581) | public NodeHeader ReadNodeHeader()
    method GetNodeForReading (line 591) | protected virtual Node* GetNodeForReading(int timeout)
    method ReturnNode (line 621) | protected virtual void ReturnNode(Node* node)
    method Read (line 662) | public virtual int Read(byte[] destination, int startIndex = 0, int ti...
    method Read (line 688) | public virtual int Read<T>(T[] destination, int startIndex = 0, int ti...
    method Read (line 712) | public virtual int Read<T>(out T destination, int timeout = 1000)
    method Read (line 744) | public virtual int Read(IntPtr destination, int length, int timeout = ...
    method Read (line 768) | public virtual int Read(Func<IntPtr, int> readFunc, int timeout = 1000)

FILE: SharedMemory/FastStructure.cs
  class FastStructure (line 36) | public static class FastStructure
    method GetPtr (line 45) | public static unsafe void* GetPtr<T>(ref T structure)
    method PtrToStructure (line 64) | public static unsafe T PtrToStructure<T>(IntPtr pointer)
    method StructureToPtr (line 83) | public static unsafe void StructureToPtr<T>(ref T structure, IntPtr po...
    method CopyTo (line 97) | public static unsafe void CopyTo<T>(ref T structure, byte[] buffer, in...
    method ToBytes (line 117) | public static unsafe byte[] ToBytes<T>(ref T structure)
    method FromBytes (line 135) | public static unsafe T FromBytes<T>(byte[] buffer, int startIndex = 0)
    method SizeOf (line 156) | public static int SizeOf<T>()
    method ReadArray (line 170) | public static unsafe void ReadArray<T>(T[] buffer, IntPtr source, int ...
    method ReadBytes (line 200) | public static unsafe void ReadBytes(byte[] buffer, IntPtr source, int ...
    method WriteArray (line 233) | public static unsafe void WriteArray<T>(IntPtr destination, T[] buffer...
    method WriteBytes (line 263) | public static unsafe void WriteBytes(IntPtr destination, byte[] buffer...
    method FastStructure (line 349) | static FastStructure()
    method BuildFunction (line 355) | private unsafe static GetPtrDelegate BuildFunction()
    method BuildLoadFromPointerFunction (line 367) | private static unsafe PtrToStructureDelegate BuildLoadFromPointerFunct...
    method BuildWriteToPointerFunction (line 380) | private static unsafe StructureToPtrDelegate BuildWriteToPointerFuncti...
    method CheckTypeCompatibility (line 394) | private static void CheckTypeCompatibility(Type t, System.Collections....
  class FastStructure (line 297) | public static class FastStructure<T>
    method GetPtr (line 45) | public static unsafe void* GetPtr<T>(ref T structure)
    method PtrToStructure (line 64) | public static unsafe T PtrToStructure<T>(IntPtr pointer)
    method StructureToPtr (line 83) | public static unsafe void StructureToPtr<T>(ref T structure, IntPtr po...
    method CopyTo (line 97) | public static unsafe void CopyTo<T>(ref T structure, byte[] buffer, in...
    method ToBytes (line 117) | public static unsafe byte[] ToBytes<T>(ref T structure)
    method FromBytes (line 135) | public static unsafe T FromBytes<T>(byte[] buffer, int startIndex = 0)
    method SizeOf (line 156) | public static int SizeOf<T>()
    method ReadArray (line 170) | public static unsafe void ReadArray<T>(T[] buffer, IntPtr source, int ...
    method ReadBytes (line 200) | public static unsafe void ReadBytes(byte[] buffer, IntPtr source, int ...
    method WriteArray (line 233) | public static unsafe void WriteArray<T>(IntPtr destination, T[] buffer...
    method WriteBytes (line 263) | public static unsafe void WriteBytes(IntPtr destination, byte[] buffer...
    method FastStructure (line 349) | static FastStructure()
    method BuildFunction (line 355) | private unsafe static GetPtrDelegate BuildFunction()
    method BuildLoadFromPointerFunction (line 367) | private static unsafe PtrToStructureDelegate BuildLoadFromPointerFunct...
    method BuildWriteToPointerFunction (line 380) | private static unsafe StructureToPtrDelegate BuildWriteToPointerFuncti...
    method CheckTypeCompatibility (line 394) | private static void CheckTypeCompatibility(Type t, System.Collections....

FILE: SharedMemory/MemoryMappedFiles/MemoryMappedFile.cs
  class MemoryMappedFile (line 46) | #if NETFULL
    method MemoryMappedFile (line 67) | private MemoryMappedFile(SafeMemoryMappedFileHandle handle)
    method CreateNew (line 86) | public static MemoryMappedFile CreateNew(String mapName, long capacity)
    method DoCreate (line 97) | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interopera...
    method CreateViewAccessor (line 128) | [SecurityCritical]
    method Dispose (line 145) | public void Dispose()
    method Dispose (line 151) | void Dispose(bool disposeManagedResources)
    method OpenExisting (line 165) | public static MemoryMappedFile OpenExisting(string mapName)

FILE: SharedMemory/MemoryMappedFiles/MemoryMappedFileAccess.cs
  type MemoryMappedFileAccess (line 38) | public enum MemoryMappedFileAccess: uint
  class MemoryMappedFileAccessExtensions (line 62) | internal static class MemoryMappedFileAccessExtensions
    method ToMapViewFileAccess (line 64) | internal static UnsafeNativeMethods.FileMapAccess ToMapViewFileAccess(...

FILE: SharedMemory/MemoryMappedFiles/MemoryMappedFileRights.cs
  type MemoryMappedFileRights (line 37) | [Flags]

FILE: SharedMemory/MemoryMappedFiles/MemoryMappedView.cs
  class MemoryMappedView (line 42) | #if NETFULL
    method MemoryMappedView (line 71) | private MemoryMappedView(SafeMemoryMappedViewHandle handle, long offse...
    method Dispose (line 89) | public void Dispose()
    method Dispose (line 95) | void Dispose(bool disposeManagedResources)
    method CreateView (line 102) | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interopera...

FILE: SharedMemory/MemoryMappedFiles/MemoryMappedViewAccessor.cs
  class MemoryMappedViewAccessor (line 42) | #if NETFULL
    method MemoryMappedViewAccessor (line 49) | internal MemoryMappedViewAccessor(MemoryMappedView memoryMappedView)
    method Dispose (line 70) | public void Dispose()
    method Dispose (line 76) | private void Dispose(bool disposeManagedResources)
    method PtrToStructure (line 83) | internal static unsafe void PtrToStructure<T>(byte* ptr, out T structure)
    method StructureToPtr (line 92) | internal static unsafe void StructureToPtr<T>(ref T structure, byte* ptr)
    method Write (line 98) | internal unsafe void Write<T>(long position, ref T structure)
    method WriteArray (line 118) | internal unsafe void WriteArray<T>(long position, T[] buffer, int inde...
    method Read (line 145) | internal unsafe void Read<T>(long position, out T structure)
    method ReadArray (line 164) | internal unsafe void ReadArray<T>(long position, T[] buffer, int index...

FILE: SharedMemory/MemoryMappedFiles/SafeMemoryMappedFileHandle.cs
  class SafeMemoryMappedFileHandle (line 39) | public sealed class SafeMemoryMappedFileHandle: SafeHandleZeroOrMinusOne...
    method SafeMemoryMappedFileHandle (line 41) | [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
    method SafeMemoryMappedFileHandle (line 47) | [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
    method ReleaseHandle (line 58) | protected override bool ReleaseHandle()

FILE: SharedMemory/MemoryMappedFiles/SafeMemoryMappedViewHandle.cs
  class SafeMemoryMappedViewHandle (line 39) | #if NETFULL
    method SafeMemoryMappedViewHandle (line 44) | [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
    method SafeMemoryMappedViewHandle (line 50) | [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
    method ReleaseHandle (line 61) | protected override bool ReleaseHandle()
    method AcquirePointer (line 78) | public unsafe void AcquirePointer(ref byte* pointer)
    method ReleasePointer (line 88) | public void ReleasePointer()

FILE: SharedMemory/RpcBuffer.cs
  type InstanceType (line 36) | internal enum InstanceType
  type RpcProtocol (line 45) | public enum RpcProtocol
  class ResponseTaskHelper (line 57) | public static class ResponseTaskHelper
    method TimeoutOrCancel (line 69) | public static async Task<RpcResponse> TimeoutOrCancel(this Task<RpcRes...
    method WaitAsync (line 102) | private static Task<RpcResponse> WaitAsync(this Task<RpcResponse> @thi...
    method DoWaitAsync (line 114) | private static async Task<RpcResponse> DoWaitAsync(Task<RpcResponse> t...
    class RpcResponseCancellationTokenTaskSource (line 123) | public sealed class RpcResponseCancellationTokenTaskSource : IDisposable
      method RpcResponseCancellationTokenTaskSource (line 134) | public RpcResponseCancellationTokenTaskSource(CancellationToken canc...
      method Dispose (line 154) | public void Dispose()
  type MessageType (line 164) | public enum MessageType : byte
  type RpcProtocolHeaderV1 (line 183) | public struct RpcProtocolHeaderV1
  class RpcRequest (line 214) | public class RpcRequest
    method RpcRequest (line 216) | internal RpcRequest() { }
  class RpcResponse (line 246) | public class RpcResponse
    method RpcResponse (line 253) | public RpcResponse(bool success, byte[] data)
  class RpcStatistics (line 273) | public class RpcStatistics
    method StartWaitRead (line 394) | internal void StartWaitRead()
    method ReadPacket (line 399) | internal void ReadPacket(int bytes)
    method StartWaitWrite (line 418) | internal void StartWaitWrite()
    method WritePacket (line 423) | internal void WritePacket(int bytes)
    method MessageReceived (line 442) | internal void MessageReceived(MessageType msgType, int size)
    method MessageSent (line 459) | internal void MessageSent(MessageType msgType, int size)
    method Timeout (line 476) | internal void Timeout()
    method DiscardResponse (line 482) | internal void DiscardResponse(ulong msgId)
    method Reset (line 491) | public void Reset()
  class RpcBuffer (line 521) | public class RpcBuffer : IDisposable
    method RpcBuffer (line 594) | public RpcBuffer(string name, Action<ulong, byte[]> remoteCallHandler,...
    method RpcBuffer (line 608) | public RpcBuffer(string name, Func<ulong, byte[], Task> asyncRemoteCal...
    method RpcBuffer (line 622) | public RpcBuffer(string name, Func<ulong, byte[], byte[]> remoteCallHa...
    method RpcBuffer (line 636) | public RpcBuffer(string name, Func<ulong, byte[], Task<byte[]>> asyncR...
    method RpcBuffer (line 649) | public RpcBuffer(string name, int bufferCapacity = 50000, RpcProtocol ...
    method CreateMessageRequest (line 727) | protected RpcRequest CreateMessageRequest()
    method RemoteRequest (line 747) | public RpcResponse RemoteRequest(byte[] args = null, int timeoutMs = d...
    method RemoteRequestAsync (line 765) | public Task<RpcResponse> RemoteRequestAsync(byte[] args = null, int ti...
    method SendMessage (line 773) | async Task<RpcResponse> SendMessage(RpcRequest request, byte[] payload...
    method SendMessage (line 790) | protected virtual Task<RpcResponse> SendMessage(MessageType msgType, R...
    method WriteProtocolV1 (line 842) | bool WriteProtocolV1(MessageType msgType, ulong msgId, byte[] msg, ulo...
    method ReadThreadV1 (line 930) | void ReadThreadV1()
    method ProcessCallHandler (line 1074) | async Task ProcessCallHandler(RpcRequest request, CancellationToken ca...
    method ThrowIfDisposedOrShutdown (line 1138) | protected void ThrowIfDisposedOrShutdown()
    method Dispose (line 1154) | public void Dispose()
    method Dispose (line 1163) | protected virtual void Dispose(bool disposeManagedResources)
    method DisposeManagedResources (line 1178) | private void DisposeManagedResources()

FILE: SharedMemory/SharedArray.cs
  class SharedArray (line 39) | [PermissionSet(SecurityAction.LinkDemand)]
    method SharedArray (line 79) | public SharedArray(string name, int length)
    method SharedArray (line 93) | public SharedArray(string name)
    method DoOpen (line 107) | protected override bool DoOpen()
    method Write (line 126) | public void Write(ref T data, int index)
    method Write (line 141) | public void Write(T[] buffer, int startIndex = 0)
    method Read (line 162) | public void Read(out T data, int index)
    method CopyTo (line 177) | public void CopyTo(T[] buffer, int startIndex = 0)
    method GetEnumerator (line 195) | public IEnumerator<T> GetEnumerator()
    method GetEnumerator (line 207) | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnume...
    method Add (line 219) | public void Add(T item)
    method Clear (line 227) | public void Clear()
    method Contains (line 237) | public bool Contains(T item)
    method Remove (line 247) | public bool Remove(T item)
    method IndexOf (line 274) | public int IndexOf(T item)
    method Insert (line 288) | public void Insert(int index, T item)
    method RemoveAt (line 297) | public void RemoveAt(int index)

FILE: SharedMemory/SharedBuffer.cs
  class SharedBuffer (line 43) | [PermissionSet(SecurityAction.LinkDemand)]
    method SharedBuffer (line 160) | protected SharedBuffer(string name, long bufferSize, bool ownsSharedMe...
    method Open (line 202) | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "C...
    method DoOpen (line 276) | protected virtual bool DoOpen()
    method InitialiseHeader (line 284) | protected void InitialiseHeader()
    method Close (line 299) | public virtual void Close()
    method DoClose (line 333) | protected virtual void DoClose()
    method Write (line 347) | protected virtual void Write<T>(ref T source, long bufferPosition = 0)
    method Write (line 359) | protected virtual void Write<T>(T[] source, long bufferPosition = 0)
    method Write (line 372) | protected virtual void Write<T>(T[] source, int index, long bufferPosi...
    method WriteArray (line 386) | protected virtual void WriteArray<T>(T[] source, int index, int count,...
    method Write (line 398) | protected virtual void Write(IntPtr source, int length, long bufferPos...
    method Write (line 412) | protected virtual void Write(Action<IntPtr> writeFunc, long bufferPosi...
    method Read (line 427) | protected virtual void Read<T>(out T data, long bufferPosition = 0)
    method Read (line 439) | protected virtual void Read<T>(T[] destination, long bufferPosition = 0)
    method ReadArray (line 453) | protected virtual void ReadArray<T>(T[] destination, int index, int co...
    method Read (line 465) | protected virtual void Read(IntPtr destination, int length, long buffe...
    method Read (line 479) | protected virtual void Read(Action<IntPtr> readFunc, long bufferPositi...
    method Dispose (line 491) | public void Dispose()
    method Dispose (line 501) | protected virtual void Dispose(bool disposeManagedResources)

FILE: SharedMemory/SharedHeader.cs
  type SharedHeader (line 39) | [StructLayout(LayoutKind.Sequential)]

FILE: SharedMemory/UnsafeNativeMethods.cs
  class UnsafeNativeMethods (line 36) | [System.Security.SuppressUnmanagedCodeSecurity]
    method UnsafeNativeMethods (line 39) | private UnsafeNativeMethods() { }
    method CopyMemory (line 48) | [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = f...
    method CopyMemoryPtr (line 52) | [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = f...
    method FormatMessage (line 59) | [DllImport("kernel32.dll", BestFitMapping = false, CharSet = CharSet.A...
    method GetMessage (line 63) | [SecurityCritical]
    type SYSTEM_INFO (line 74) | [StructLayout(LayoutKind.Sequential)]
    type _PROCESSOR_INFO_UNION (line 89) | [StructLayout(LayoutKind.Explicit)]
    type FileMapAccess (line 100) | [Flags]
    type FileMapProtection (line 110) | [Flags]
    method CloseHandle (line 140) | [DllImport("kernel32.dll", CharSet = CharSet.None, SetLastError = true)]
    method CreateFileMapping (line 144) | [DllImport("kernel32.dll", BestFitMapping = false, CharSet = CharSet.A...
    method CreateFileMapping (line 147) | internal static SafeMemoryMappedFileHandle CreateFileMapping(SafeFileH...
    method GetSystemInfo (line 154) | [DllImport("kernel32.dll")]
    method MapViewOfFile (line 157) | [DllImport("kernel32.dll", SetLastError = true)]
    method MapViewOfFile (line 164) | internal static SafeMemoryMappedViewHandle MapViewOfFile(SafeMemoryMap...
    method OpenFileMapping (line 171) | [DllImport("kernel32.dll", BestFitMapping = false, CharSet = CharSet.A...
    method UnmapViewOfFile (line 177) | [DllImport("kernel32.dll", SetLastError = true)]

FILE: SharedMemory/Utilities/ArraySlice.cs
  type ArraySlice (line 39) | [PermissionSet(SecurityAction.LinkDemand)]
    method ArraySlice (line 53) | public ArraySlice(IList<T> list)
    method ArraySlice (line 72) | public ArraySlice(IList<T> list, int offset, int count)
    method GetHashCode (line 125) | public override int GetHashCode()
    method Equals (line 136) | public override bool Equals(Object obj)
    method Equals (line 147) | public bool Equals(ArraySlice<T> obj)
    method IndexOf (line 204) | public int IndexOf(T item)
    method Insert (line 216) | void IList<T>.Insert(int index, T item)
    method RemoveAt (line 221) | void IList<T>.RemoveAt(int index)
    method Add (line 238) | void ICollection<T>.Add(T item)
    method Clear (line 243) | void ICollection<T>.Clear()
    method Contains (line 248) | bool ICollection<T>.Contains(T item)
    method CopyTo (line 256) | void ICollection<T>.CopyTo(T[] array, int arrayIndex)
    method Remove (line 261) | bool ICollection<T>.Remove(T item)
    method GetEnumerator (line 268) | IEnumerator<T> IEnumerable<T>.GetEnumerator()
    method GetEnumerator (line 278) | IEnumerator IEnumerable.GetEnumerator()
    class ArraySliceEnumerator (line 287) | [Serializable]
      method ArraySliceEnumerator (line 295) | internal ArraySliceEnumerator(ArraySlice<T> arraySlice)
      method MoveNext (line 303) | public bool MoveNext()
      method Reset (line 331) | void IEnumerator.Reset()
      method Dispose (line 336) | public void Dispose()

FILE: SharedMemory/Utilities/ExpandingArray.cs
  class ExpandingArray (line 18) | public class ExpandingArray<T> : IList<T>
    method ExpandingArray (line 57) | public ExpandingArray(Func<int, IList<T>> allocator = null, int finalC...
    method GetBucket (line 69) | private IList<T> GetBucket(int bucketIndex)
    method GetBucketIndex (line 86) | public static int GetBucketIndex(int index)
    method GetLocalIndex (line 98) | private int GetLocalIndex(int globalIndex, out IList<T> bucket)
    method Add (line 109) | public void Add(T item)
    method Clear (line 144) | public void Clear()
    method Contains (line 153) | public bool Contains(T item)
    method CopyTo (line 166) | public void CopyTo(T[] array, int arrayIndex)
    method Remove (line 175) | public bool Remove(T item)
    method GetEnumerator (line 196) | public IEnumerator<T> GetEnumerator()
    method GetEnumerator (line 203) | IEnumerator IEnumerable.GetEnumerator()
    method IndexOf (line 212) | public int IndexOf(T item)
    method Insert (line 227) | public void Insert(int index, T item)
    method RemoveAt (line 237) | public void RemoveAt(int index)
Condensed preview — 44 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (434K chars).
[
  {
    "path": ".gitignore",
    "chars": 375,
    "preview": "#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*"
  },
  {
    "path": "Examples/ClientTest/ClientTest.csproj",
    "chars": 320,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFrameworks>netcoreapp3"
  },
  {
    "path": "Examples/ClientTest/Program.cs",
    "chars": 6626,
    "preview": "// SharedMemory (File: ClientTest\\Program.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Perm"
  },
  {
    "path": "Examples/RpcTest/Program.cs",
    "chars": 4132,
    "preview": "// SharedMemory (File: RpcTest\\Program.cs)\n// Copyright (c) 2020 Justin Stenning\n// http://spazzarama.com\n//\n// Permiss"
  },
  {
    "path": "Examples/RpcTest/RpcTest.csproj",
    "chars": 375,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFrameworks>netcoreapp3"
  },
  {
    "path": "Examples/ServerTest/Program.cs",
    "chars": 5408,
    "preview": "// SharedMemory (File: ServerTest\\Program.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// Perm"
  },
  {
    "path": "Examples/ServerTest/ServerTest.csproj",
    "chars": 320,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFrameworks>netcoreapp3"
  },
  {
    "path": "Examples/SingleProcess/CommandLineParser.cs",
    "chars": 57268,
    "preview": "//////////////////////////////////////////////////////////////////////////////\n//    Command Line Argument Parser\n//   "
  },
  {
    "path": "Examples/SingleProcess/Program.cs",
    "chars": 7831,
    "preview": "// SharedMemory (File: SingleProcess\\Program.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n// P"
  },
  {
    "path": "Examples/SingleProcess/SingleProcess.csproj",
    "chars": 320,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFrameworks>netcoreapp3"
  },
  {
    "path": "LICENSE.md",
    "chars": 1351,
    "preview": "SharedMemory\n\nCopyright (c) 2014-2020 Justin Stenning\nhttp://spazzarama.com\n\nPermission is hereby granted, free of charg"
  },
  {
    "path": "README.md",
    "chars": 6854,
    "preview": "SharedMemory\n============\n\nC# shared memory classes for sharing data between processes (Array, Buffer, Circular Buffer a"
  },
  {
    "path": "SharedMemory/BufferReadWrite.cs",
    "chars": 8587,
    "preview": "// SharedMemory (File: SharedMemory\\bufferreadwrite.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n"
  },
  {
    "path": "SharedMemory/BufferWithLocks.cs",
    "chars": 14436,
    "preview": "// SharedMemory (File: SharedMemory\\BufferWithLocks.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n"
  },
  {
    "path": "SharedMemory/CircularBuffer.cs",
    "chars": 33331,
    "preview": "// SharedMemory (File: SharedMemory\\CircularBuffer.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n/"
  },
  {
    "path": "SharedMemory/FastStructure.cs",
    "chars": 20540,
    "preview": "// SharedMemory (File: SharedMemory\\FastStructure.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//"
  },
  {
    "path": "SharedMemory/MemoryMappedFiles/MemoryMappedFile.cs",
    "chars": 7805,
    "preview": "// SharedMemory (File: SharedMemory\\MemoryMappedFile.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com"
  },
  {
    "path": "SharedMemory/MemoryMappedFiles/MemoryMappedFileAccess.cs",
    "chars": 3331,
    "preview": "// SharedMemory (File: SharedMemory\\MemoryMappedFileAccess.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzara"
  },
  {
    "path": "SharedMemory/MemoryMappedFiles/MemoryMappedFileRights.cs",
    "chars": 2191,
    "preview": "// SharedMemory (File: SharedMemory\\MemoryMappedFileRights.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzara"
  },
  {
    "path": "SharedMemory/MemoryMappedFiles/MemoryMappedView.cs",
    "chars": 5511,
    "preview": "// SharedMemory (File: SharedMemory\\MemoryMappedView.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com"
  },
  {
    "path": "SharedMemory/MemoryMappedFiles/MemoryMappedViewAccessor.cs",
    "chars": 6753,
    "preview": "// SharedMemory (File: SharedMemory\\MemoryMappedViewAccessor.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazza"
  },
  {
    "path": "SharedMemory/MemoryMappedFiles/SafeMemoryMappedFileHandle.cs",
    "chars": 2711,
    "preview": "// SharedMemory (File: SharedMemory\\safememorymappedfilehandle.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spaz"
  },
  {
    "path": "SharedMemory/MemoryMappedFiles/SafeMemoryMappedViewHandle.cs",
    "chars": 3429,
    "preview": "// SharedMemory (File: SharedMemory\\safememorymappedviewhandle.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spaz"
  },
  {
    "path": "SharedMemory/RpcBuffer.cs",
    "chars": 49437,
    "preview": "// SharedMemory (File: SharedMemory\\RpcBuffer.cs)\n// Copyright (c) 2020 Justin Stenning\n// http://spazzarama.com\n//\n// "
  },
  {
    "path": "SharedMemory/SharedArray.cs",
    "chars": 12082,
    "preview": "// SharedMemory (File: SharedMemory\\SharedArray.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n/"
  },
  {
    "path": "SharedMemory/SharedBuffer.cs",
    "chars": 22616,
    "preview": "// SharedMemory (File: SharedMemory\\SharedBuffer.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n"
  },
  {
    "path": "SharedMemory/SharedHeader.cs",
    "chars": 2684,
    "preview": "// SharedMemory (File: SharedMemory\\SharedHeader.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama.com\n//\n"
  },
  {
    "path": "SharedMemory/SharedMemory.csproj",
    "chars": 2203,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks>netstandard2.1;netstandard2.0;net47;net46;ne"
  },
  {
    "path": "SharedMemory/SharedMemory.licenseheader",
    "chars": 1671,
    "preview": "extensions: designer.cs generated.cs\nextensions: .cs .cpp .h\n// SharedMemory (File: %Project%\\%FileName%)\n// Copyright ("
  },
  {
    "path": "SharedMemory/UnsafeNativeMethods.cs",
    "chars": 7928,
    "preview": "// SharedMemory (File: SharedMemory\\UnsafeNativeMethods.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama."
  },
  {
    "path": "SharedMemory/Utilities/ArraySlice.cs",
    "chars": 12004,
    "preview": "// SharedMemory (File: SharedMemory\\Utilities\\ArraySlice.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzarama"
  },
  {
    "path": "SharedMemory/Utilities/ExpandingArray.cs",
    "chars": 13084,
    "preview": "using System;\r\nusing System.Collections;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\n\r\n"
  },
  {
    "path": "SharedMemory.Tests/ArraySliceTests.cs",
    "chars": 5178,
    "preview": "// SharedMemory (File: SharedMemoryTests\\ArraySliceTests.cs)\r\n// Copyright (c) 2014 Justin Stenning\r\n// http://spazzara"
  },
  {
    "path": "SharedMemory.Tests/ArrayTests.cs",
    "chars": 13904,
    "preview": "// SharedMemory (File: SharedMemoryTests\\ArrayTests.cs)\r\n// Copyright (c) 2014 Justin Stenning\r\n// http://spazzarama.co"
  },
  {
    "path": "SharedMemory.Tests/BufferReadWriteTests.cs",
    "chars": 2832,
    "preview": "using System;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace SharedMemoryTests\n{\n    [TestClass]\n    pu"
  },
  {
    "path": "SharedMemory.Tests/CircularBufferTests.cs",
    "chars": 34261,
    "preview": "// SharedMemory (File: SharedMemoryTests\\CircularBufferTests.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazza"
  },
  {
    "path": "SharedMemory.Tests/ExpandingArrayTests.cs",
    "chars": 2387,
    "preview": "using System;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\r\nusing Share"
  },
  {
    "path": "SharedMemory.Tests/FastStructureTests.cs",
    "chars": 6364,
    "preview": "// SharedMemory (File: SharedMemoryTests\\FastStructureTests.cs)\n// Copyright (c) 2014 Justin Stenning\n// http://spazzar"
  },
  {
    "path": "SharedMemory.Tests/RpcBufferTests.cs",
    "chars": 16503,
    "preview": "// SharedMemory (File: SharedMemoryTests\\RpcBufferTests.cs)\n// Copyright (c) 2020 Justin Stenning\n// http://spazzarama."
  },
  {
    "path": "SharedMemory.Tests/SharedMemory.Tests.csproj",
    "chars": 716,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks>netcoreapp3.0;netcoreapp2.0;net47;net46;net45"
  },
  {
    "path": "SharedMemory.nuspec",
    "chars": 4279,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<package xmlns=\"http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd\">\n    "
  },
  {
    "path": "SharedMemory.sln",
    "chars": 4084,
    "preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.2972"
  },
  {
    "path": "appveyor-develop.yml",
    "chars": 1674,
    "preview": "version: 2.2.{build}\npull_requests:\n  do_not_increment_build_number: true\nbranches:\n  only:\n  - master\nskip_tags: true\ni"
  },
  {
    "path": "appveyor.yml",
    "chars": 1789,
    "preview": "version: 2.3.{build}\npull_requests:\n  do_not_increment_build_number: true\nbranches:\n  only:\n  - master\nskip_tags: true\ni"
  }
]

About this extraction

This page contains the full source code of the justinstenning/SharedMemory GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 44 files (407.7 KB), approximately 86.7k tokens, and a symbol index with 396 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!