Created
November 30, 2016 04:56
-
-
Save ufcpp/ecf02272d30c140da5bfada435bed212 to your computer and use it in GitHub Desktop.
ref returnsを使えば、unamagedなメモリ領域に対して、safeな(unsafeが付いてない)コンテキストから読み書き可能
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
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