Created
October 28, 2023 09:46
-
-
Save hsytkm/d59e0a2378f0fb32606251e7e5b9ff3e to your computer and use it in GitHub Desktop.
Memory-Mapped Files and Overlaid Structs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// [Memory-Mapped Files and Overlaid Structs](https://blog.stephencleary.com/2023/09/memory-mapped-files-overlaid-structs.html) | |
// [Padding for Overlaid Structs](https://blog.stephencleary.com/2023/10/padding-for-overlaid-structs.html) | |
using System; | |
using System.Buffers.Binary; | |
using System.IO; | |
using System.IO.MemoryMappedFiles; | |
using System.Runtime.CompilerServices; | |
using FileStream file = new( | |
@"tmp.dat", | |
FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None, | |
4096, FileOptions.RandomAccess); | |
using MemoryMappedFile mapping = MemoryMappedFile.CreateFromFile( | |
file, null, Unsafe.SizeOf<Data>(), | |
MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, leaveOpen: true); | |
using MemoryMappedViewAccessor view = mapping.CreateViewAccessor(); | |
using Overlay overlay = new(view); | |
ref Data data = ref overlay.As<Data>(); | |
Console.WriteLine(data); | |
data.First++; | |
data.Second += 100; | |
Console.WriteLine(data); | |
public sealed unsafe class Overlay : IDisposable | |
{ | |
private bool _disposed; | |
private readonly MemoryMappedViewAccessor _view; | |
private readonly byte* _pointer; | |
public Overlay(MemoryMappedViewAccessor view) | |
{ | |
_view = view; | |
view.SafeMemoryMappedViewHandle.AcquirePointer(ref _pointer); | |
} | |
public void Dispose() | |
{ | |
if (_disposed) | |
return; | |
_view.SafeMemoryMappedViewHandle.ReleasePointer(); | |
_disposed = true; | |
} | |
public ref T As<T>() where T : struct | |
{ | |
if (_disposed) | |
throw new NotSupportedException("Already disposed."); | |
return ref Unsafe.AsRef<T>(_pointer); | |
} | |
} | |
public static class OverlayHelpers | |
{ | |
// ReadBigEndian | |
public static short ReadBigEndian(short bigEndian) => | |
BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(bigEndian) : bigEndian; | |
public static ushort ReadBigEndian(ushort bigEndian) => | |
BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(bigEndian) : bigEndian; | |
public static int ReadBigEndian(int bigEndian) => | |
BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(bigEndian) : bigEndian; | |
public static uint ReadBigEndian(uint bigEndian) => | |
BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(bigEndian) : bigEndian; | |
public static long ReadBigEndian(long bigEndian) => | |
BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(bigEndian) : bigEndian; | |
public static ulong ReadBigEndian(ulong bigEndian) => | |
BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(bigEndian) : bigEndian; | |
// WriteBigEndian | |
public static void WriteBigEndian(out short bigEndian, short value) => | |
bigEndian = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(value) : value; | |
public static void WriteBigEndian(out ushort bigEndian, ushort value) => | |
bigEndian = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(value) : value; | |
public static void WriteBigEndian(out int bigEndian, int value) => | |
bigEndian = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(value) : value; | |
public static void WriteBigEndian(out uint bigEndian, uint value) => | |
bigEndian = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(value) : value; | |
public static void WriteBigEndian(out long bigEndian, long value) => | |
bigEndian = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(value) : value; | |
public static void WriteBigEndian(out ulong bigEndian, ulong value) => | |
bigEndian = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(value) : value; | |
// ReadLittleEndian | |
public static short ReadLittleEndian(short littleEndian) => | |
BitConverter.IsLittleEndian ? littleEndian : BinaryPrimitives.ReverseEndianness(littleEndian); | |
public static ushort ReadLittleEndian(ushort littleEndian) => | |
BitConverter.IsLittleEndian ? littleEndian : BinaryPrimitives.ReverseEndianness(littleEndian); | |
public static int ReadLittleEndian(int littleEndian) => | |
BitConverter.IsLittleEndian ? littleEndian : BinaryPrimitives.ReverseEndianness(littleEndian); | |
public static uint ReadLittleEndian(uint littleEndian) => | |
BitConverter.IsLittleEndian ? littleEndian : BinaryPrimitives.ReverseEndianness(littleEndian); | |
public static long ReadLittleEndian(long littleEndian) => | |
BitConverter.IsLittleEndian ? littleEndian : BinaryPrimitives.ReverseEndianness(littleEndian); | |
public static ulong ReadLittleEndian(ulong littleEndian) => | |
BitConverter.IsLittleEndian ? littleEndian : BinaryPrimitives.ReverseEndianness(littleEndian); | |
// WriteLittleEndian | |
public static void WriteLittleEndian(out short littleEndian, short value) => | |
littleEndian = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); | |
public static void WriteLittleEndian(out ushort littleEndian, ushort value) => | |
littleEndian = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); | |
public static void WriteLittleEndian(out int littleEndian, int value) => | |
littleEndian = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); | |
public static void WriteLittleEndian(out uint littleEndian, uint value) => | |
littleEndian = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); | |
public static void WriteLittleEndian(out long littleEndian, long value) => | |
littleEndian = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); | |
public static void WriteLittleEndian(out ulong littleEndian, ulong value) => | |
littleEndian = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); | |
} | |
public struct Data | |
{ | |
// Layout | |
private int _first; | |
private int _second; | |
// Convenience accessors | |
public int First | |
{ | |
readonly get => OverlayHelpers.ReadLittleEndian(_first); | |
set => OverlayHelpers.WriteLittleEndian(out _first, value); | |
} | |
public int Second | |
{ | |
readonly get => OverlayHelpers.ReadLittleEndian(_second); | |
set => OverlayHelpers.WriteLittleEndian(out _second, value); | |
} | |
public override readonly string ToString() => $"{First}, {Second}"; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment