Skip to content

Instantly share code, notes, and snippets.

@hsytkm
Created October 28, 2023 09:46
Show Gist options
  • Save hsytkm/d59e0a2378f0fb32606251e7e5b9ff3e to your computer and use it in GitHub Desktop.
Save hsytkm/d59e0a2378f0fb32606251e7e5b9ff3e to your computer and use it in GitHub Desktop.
Memory-Mapped Files and Overlaid Structs
// [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