Skip to content

Instantly share code, notes, and snippets.

@ufcpp
Created November 30, 2016 04:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ufcpp/ecf02272d30c140da5bfada435bed212 to your computer and use it in GitHub Desktop.
Save ufcpp/ecf02272d30c140da5bfada435bed212 to your computer and use it in GitHub Desktop.
ref returnsを使えば、unamagedなメモリ領域に対して、safeな(unsafeが付いてない)コンテキストから読み書き可能
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// unmanagedなメモリ領域に対して、クラスの外からはunsafe不要で読み書きするためのクラス。
/// </summary>
/// <remarks>
/// デモ用ってことで手抜き。
/// 構造体にしてるんでメモリ効率はいいけど、finalizer呼ばれないんで、Dispose しなかったらリーク起こす。
/// indexの範囲チェックもしてない。
/// nullチェックもしてないので、defaultで初期化されるとぬるぽ。
///
/// このデモでは<see cref="Marshal.AllocHGlobal(int)"/>でメモリ確保しているものの、
/// P/Invoke で完全にネイティブコードからもらってきたメモリ領域に対しても同様のことが可能。
/// </remarks>
/// <typeparam name="T"></typeparam>
unsafe struct UnmanagedBuffer<T> : IDisposable
where T : struct
{
byte* _buffer;
/// <summary>
/// sizeof(T) × <paramref name="capacity"/> 長の unmanaged メモリを確保。
/// </summary>
/// <param name="capacity"></param>
public UnmanagedBuffer(int capacity)
{
var numBytes = Unsafe.SizeOf<T>() * capacity;
_buffer = (byte*)Marshal.AllocHGlobal(numBytes);
}
/// <summary>
/// unmanged メモリ内に対して、T 型の参照としてアクセス。
/// </summary>
/// <param name="index">確保したメモリ領域がTの配列だと思って、何番目の要素にアクセスするか。</param>
/// <returns>その要素への参照。</returns>
public ref T this[int index] => ref Unsafe.AsRef<T>(_buffer + Unsafe.SizeOf<T>() * index);
/// <summary>
/// unmanged メモリ内に対して、byte 型の参照としてアクセス。
/// </summary>
/// <param name="index">確保したメモリの何byte目にアクセスするか。</param>
/// <returns>そのbyteへの参照。</returns>
public ref byte GetByte(int index) => ref *(_buffer + index);
/// <summary>
/// メモリ解放。
/// </summary>
public void Dispose()
{
Marshal.FreeHGlobal((IntPtr)_buffer);
}
}
/// <summary>
/// 動作確認用に使うわざとらしい構造体。
/// </summary>
struct Point
{
public int X;
public int Y;
public override string ToString() => $"({X.ToString("X8")}, {Y.ToString("X8")})";
}
class Program
{
static void Main(string[] args)
{
using (var buffer = new UnmanagedBuffer<Point>(3))
{
// Point に書き込んで、byte 列として表示
buffer[0].X = 1;
buffer[0].Y = 2;
buffer[1].X = 3;
buffer[1].Y = 4;
buffer[2].X = 5;
buffer[2].Y = 6;
for (int i = 0; i < 3 * 8; i++)
{
Console.Write(buffer.GetByte(i).ToString("X2"));
Console.Write(" ");
}
Console.WriteLine();
// byte 列に書き込んで、Point として表示
for (int i = 0; i < 3 * 8; i++)
{
buffer.GetByte(i) = (byte)i;
}
Console.WriteLine(buffer[0]);
Console.WriteLine(buffer[1]);
Console.WriteLine(buffer[2]);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment