2016-03-08T06:51:00の更新内容

programming/netfx/arrays/4_bytearray/index.wiki.txt

current previous
1,922 0,0
+
${smdncms:title,バイト列操作}
+
${smdncms:keywords,System.Buffer,System.BitConverter,バイナリ,memcpy,memmove}
+
${smdncms:document_versions,codelang=cs,codelang=vb}
+

          
+

          
+
#navi(..)
+

          
+
.NET Frameworkにおいてバッファなどのバイト列を扱うクラスには[[MemoryStream>programming/netfx/stream/1_2_memorystream]]が存在しますが、&msdn(netfx,type,System.Buffer){Bufferクラス};のメソッドを用いることによって[[プリミティブ型の配列>#primitive_types]]を直接バイト列として操作することができます。 例えば、``int``型の配列に対して[[バイト単位で値の取得・設定>#Buffer.GetByteSetByte]]をしたり、``memcpy``のように配列の一部を[[バイト列としてコピーする>#Buffer.BlockCopy]]ことができます。
+

          
+
こういった操作はシフト演算やポインタを介して行うこともできますが、Bufferクラスを用いればポインタ、つまり``unsafe``コンテキストによるアンセーフコードを一切用いずに記述することができます。
+

          
+
#relevantdocs
+

          
+
-[[programming/netfx/stream]]
+
--[[programming/netfx/stream/3_binaryreader_binarywriter]]
+
--[[programming/netfx/stream/1_2_memorystream]]
+
--[[programming/netfx/stream/1_3_unmanagedmemorystream]]
+
-[[programming/netfx/struct]]
+
--[[programming/netfx/struct/2_sizeof]]
+
--[[programming/netfx/struct/3_arrayfields]]
+
-[[programming/netfx/conversion/0_basetype#ByteArrayConversion]]
+
-[[programming/netfx/tips/convert_struct_and_bytearray]]
+
-[[programming/netfx/tips/get_textbytecount]]
+
-[[programming/mono/Mono.Simd]]
+

          
+
#relevantdocs-end
+

          
+
#adunit
+

          
+
*Bufferクラス [#System.Buffer]
+
&msdn(netfx,type,System.Buffer){Bufferクラス};は配列をバッファとして扱い、バイト単位での操作を行うためのメソッドを持ったクラスです。 例えば、``int``型配列に対して、[[1バイト単位で読み取り・書き込み>#Buffer.GetByteSetByte]]をしたり、``memcpy``・``memcpy_s``のように[[メモリブロックのコピーを行う>#Buffer.BlockCopy]]ためのメソッドなどが提供されています。
+

          
+
#remarks
+
Bufferクラスは、リングバッファなどの''バッファ機能を実装するクラスではありません''。 バッファとなるメモリ領域を確保し、確保した領域に読み書きする目的には&msdn(netfx,type,System.Runtime.InteropServices.SafeBuffer){SafeBufferクラス};、ストリームへの読み書きに対してバッファリングを行う目的には&msdn(netfx,type,System.IO.BufferedStream){BufferedStreamクラス};など、バッファ機能を扱うクラスは別に存在します。
+
#remarks-end
+

          
+

          
+
**Bufferクラスで扱える配列型 [#primitive_types]
+
Bufferクラスは''プリミティブ型''の配列に対してのみ用いることができます。 その他の型の配列、構造体やクラスの配列に対しては用いることができません。 .NET Frameworkでは``byte``, ``int``, ``char``, ``double``, &msdn(netfx,type,System.IntPtr){IntPtr};などの型がプリミティブ型として扱われます。 ここで言うプリミティブ型とは言語の組み込み型とは異なります。 例えば``decimal``や``int?``は組み込み型ですがプリミティブ型ではありません。
+

          
+
|*Bufferクラスで扱えるプリミティブ型
+
|~数値型|~整数型|``sbyte``, ``short``, ``int``, ``long``&br;``byte``, ``ushort``, ``uint``, ``ulong``|
+
|~|~実数型|``single``, ``double``|
+
|~|~ポインタ型|``IntPtr``, ``UIntPtr``|
+
|>|~文字型|``char``|
+
|>|~ブール型|``bool``|
+

          
+
#remarks
+
プリミティブ型についてや型の分類等について詳しくは[[programming/netfx/basic_types/0_characteristics]]を参照してください。
+
#remarks-end
+

          
+
**配列のバイト列操作
+
***バイト単位での値の設定・取得 (GetByteメソッド・SetByteメソッド) [#Buffer.GetByteSetByte]
+
&msdn(netfx,member,System.Buffer.GetByte){Buffer.GetByteメソッド};および&msdn(netfx,member,System.Buffer.SetByte){Buffer.SetByteメソッド};を用いると、プリミティブ型配列に対してバイト単位での値の取得・設定を行うことができます。 これは、任意のプリミティブ型配列をバイト配列と同様に扱うことができることを意味します。
+

          
+
例えば配列``int[]``をポインタ``byte*``にキャストしてバイト列として扱うような操作は、このメソッドによって代用することができます。 したがって、GetByteメソッド・SetByteメソッドを用いれば、``unsafe``なコードの記述を避けることができます。
+

          
+
#code(cs,Buffer.GetByteメソッドを使って配列をバイト列として扱い、値を取得する){{
+
using System;
+
using System.Linq;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    int[] arr = {0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f};
+

          
+
    // (配列の内容を16進数で表示)
+
    Console.WriteLine(string.Join(" ", arr.Select(e => e.ToString("X8"))));
+

          
+
    // 配列arrの先頭から4バイト目(=1番目の要素の0バイト目)の値を取得する
+
    // ( "*((*byte)arr + 4)" に相当する操作)
+
    Console.WriteLine("GetByte(arr, 4) = {0:X2}", Buffer.GetByte(arr, 4));
+

          
+
    // 配列arrの先頭から7バイト目(=1番目の要素の3バイト目)の値を取得する
+
    // ( "*((*byte)arr + 7)" に相当する操作)
+
    Console.WriteLine("GetByte(arr, 7) = {0:X2}", Buffer.GetByte(arr, 7));
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
00010203 04050607 08090A0B 0C0D0E0F
+
GetByte(arr, 4) = 07
+
GetByte(arr, 7) = 04
+
}}
+

          
+
#remarks
+
上記の例において、配列内の各値は''リトルエンディアン''で格納されている点に注意してください。 配列&var{arr};のバイト表現を図式化すると次にようになります。
+

          
+
#matrix
+
|*配列&var{arr};の内容
+
|~オフセット(バイト)|~0|~1|~2|~3|~4|~5|~6|~7|~8|~9|~10|~11|~12|~13|~14|~15|
+
|~要素値|>|>|>|CENTER:0x00010203|>|>|>|CENTER:0x04050607|>|>|>|CENTER:0x08090A0B|>|>|>|CENTER:0x0C0D0E0F|
+
|~バイト値|0x03|0x02|0x01|0x00|0x07|0x06|0x05|0x04|0x0B|0x0A|0x09|0x08|0x0F|0x0E|0x0D|0x0C|
+
#matrix-end
+

          
+
#remarks-end
+

          
+

          
+

          
+

          
+
#code(cs,Buffer.SetByteメソッドを使って配列をバイト列として扱い、値を設定する){{
+
using System;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    int[] arr = {0, 1, 2, 3};
+

          
+
    Console.WriteLine(string.Join(", ", arr));
+

          
+
    // 配列arrの1バイト目に値0x01を設定する
+
    // ( "*((*byte)arr + 1) = 0x01" に相当する操作)
+
    Buffer.SetByte(arr, 1, 0x01);
+

          
+
    Console.WriteLine(string.Join(", ", arr));
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
0, 1, 2, 3
+
256, 1, 2, 3
+
}}
+

          
+

          
+
***配列のバイト数の取得 (ByteLengthメソッド) [#Buffer.ByteLength]
+
GetByteメソッド・SetByteメソッドに加えて、&msdn(netfx,member,System.Buffer.ByteLength){Buffer.ByteLengthメソッド};を使うと配列のバイト数を取得することができます。 配列のバイト数は、``sizeof``演算子で型のサイズを求め、配列の長さとの積をとることでも求めることができますが、このメソッドでは配列の型が何であるかを意識しなくてもメソッド呼び出しのみで配列のバイト数を求めることができます。
+

          
+
#code(cs,Buffer.ByteLengthメソッドを使って配列のバイト数を求める){{
+
using System;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    byte[] bytes = {0, 1, 2, 3};
+
    short[] shorts = {0, 1, 2, 3};
+
    int[] ints = {0, 1, 2, 3};
+

          
+
    // Buffer.ByteLengthメソッドで各配列のバイト数を求める
+
    // (sizeof(T) * arr.Lengthとしても求められるが、このメソッドでは配列の型を意識する必要がなく
+
    // 配列を単なるバイト列とみなしてそのサイズを取得できる)
+
    Console.WriteLine(Buffer.ByteLength(bytes));
+
    Console.WriteLine(Buffer.ByteLength(shorts));
+
    Console.WriteLine(Buffer.ByteLength(ints));
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
4
+
8
+
16
+
}}
+

          
+
GetByteメソッド・SetByteメソッドと同様、ByteLengthメソッドもプリミティブ型配列に対してのみ用いることができます。
+

          
+
#remarks
+
C#における``sizeof``演算子については[[programming/netfx/struct/2_sizeof#sizeof]]を参照してください。
+
#remarks-end
+

          
+

          
+
***Bufferクラスによるバイト列操作の例
+
以下ではGetByteメソッド・SetByteメソッドを使った例を掲載します。 いずれもパフォーマンス上の観点からはポインタ演算やシフト演算を用いた方がよいものですが、GetByteメソッド・SetByteメソッドを用いることで``unsafe``コンテキストを使った記述を排除できる=マネージドコードのみを用いても記述できることを示しています。
+

          
+
#code(cs,Buffer.GetByteメソッドを使って配列のバイト表現を表示する){{
+
using System;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    short[] arr = new short[] {0, 1, 2, 3};
+

          
+
    // 配列のバイト数を取得する
+
    var sizeOfArray = Buffer.ByteLength(arr);
+
    // sizeof演算子を使って次のように求めることもできる
+
    // (ただし、配列の型は既知である必要がある)
+
    //var sizeOfArray = sizeof(short) * arr.Length
+

          
+
    // Buffer.GetByteメソッドを使って配列のバイト表現を出力する
+
    for (var offset = 0; offset < sizeOfArray; offset++) {
+
      Console.Write("0x{0:x2} ", Buffer.GetByte(arr, offset));
+
    }
+
    Console.WriteLine();
+

          
+
    // 上記の処理をポインタを使って記述すると次のようになる
+
    // (Buffer.GetByteメソッドを使えば、unsafeコンテキストやポインタを
+
    // 用いずにこのような処理を記述することができる)
+
    unsafe {
+
      fixed (void* ptr = arr) {
+
        for (var offset = 0; offset < sizeOfArray; offset++) {
+
          Console.Write("0x{0:x2} ", *((byte*)ptr + offset));
+
        }
+
        Console.WriteLine();
+
      }
+
    }
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
0x00 0x00 0x01 0x00 0x02 0x00 0x03 0x00 
+
0x00 0x00 0x01 0x00 0x02 0x00 0x03 0x00 
+
}}
+

          
+

          
+

          
+
#code(cs,Buffer.SetByteメソッドを使って32bit整数値のエンディアンネスを逆転する){{
+
using System;
+
using System.Linq;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    uint[] arr = new uint[] {0x01234567, 0xff00ff00, 0xdeadbeef};
+

          
+
    Console.WriteLine(string.Join(" ", arr.Select(e => e.ToString("X8"))));
+

          
+
    for (var offset = 0; offset < Buffer.ByteLength(arr); offset += 4) {
+
      byte b0 = Buffer.GetByte(arr, offset + 0);
+
      byte b1 = Buffer.GetByte(arr, offset + 1);
+
      byte b2 = Buffer.GetByte(arr, offset + 2);
+
      byte b3 = Buffer.GetByte(arr, offset + 3);
+

          
+
      Buffer.SetByte(arr, offset + 0, b3);
+
      Buffer.SetByte(arr, offset + 1, b2);
+
      Buffer.SetByte(arr, offset + 2, b1);
+
      Buffer.SetByte(arr, offset + 3, b0);
+
    }
+

          
+
    Console.WriteLine(string.Join(" ", arr.Select(e => e.ToString("X8"))));
+

          
+
    // エンディアンネスの変換にはSystem.Net.IPAddressクラスのNetworkToHostOrderメソッド
+
    // またはHostToNetworkOrderメソッドを使うことができる
+
    for (var index = 0; index < arr.Length; index++) {
+
      arr[index] = (uint)System.Net.IPAddress.NetworkToHostOrder((int)arr[index]);
+
    }
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
01234567 FF00FF00 DEADBEEF
+
67452301 00FF00FF EFBEADDE
+
}}
+

          
+

          
+

          
+
#code(cs,Buffer.SetByteメソッドを使って32bitRGBカラーの各ピクセルを飽和加算する){{
+
using System;
+
using System.Linq;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    int[] pixels1 = new int[] {0x00808080, 0x0060a0c0, 0x00c09060, 0x00206040}; // 32bit RGB 4pixels
+
    int[] pixels2 = new int[] {0x00997788, 0x00444444, 0x00666666, 0x00333333}; // 32bit RGB 4pixels
+

          
+
    Console.WriteLine(string.Join(" ", pixels1.Select(pixel => pixel.ToString("X8"))));
+
    Console.WriteLine(string.Join(" ", pixels2.Select(pixel => pixel.ToString("X8"))));
+

          
+
    int[] pixels = new int[4];
+

          
+
    for (var offset = 0; offset < Buffer.ByteLength(pixels); offset += 4) {
+
      // R, G, Bの各色素を飽和加算する
+
      byte b = (byte)Math.Min((int)Buffer.GetByte(pixels1, offset + 0) + (int)Buffer.GetByte(pixels2, offset + 0), 0xff); // 各intの0バイト目
+
      byte g = (byte)Math.Min((int)Buffer.GetByte(pixels1, offset + 1) + (int)Buffer.GetByte(pixels2, offset + 1), 0xff); // 各intの1バイト目
+
      byte r = (byte)Math.Min((int)Buffer.GetByte(pixels1, offset + 2) + (int)Buffer.GetByte(pixels2, offset + 2), 0xff); // 各intの2バイト目
+

          
+
      // 結果を配列へ格納する
+
      Buffer.SetByte(pixels, offset + 0, b); // 各intの0バイト目
+
      Buffer.SetByte(pixels, offset + 1, g); // 各intの1バイト目
+
      Buffer.SetByte(pixels, offset + 2, r); // 各intの2バイト目
+
      Buffer.SetByte(pixels, offset + 3, 0); // 各intの3バイト目
+
    }
+

          
+
    Console.WriteLine(string.Join(" ", pixels.Select(pixel => pixel.ToString("X8"))));
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
00808080 0060A0C0 00C09060 00206040
+
00997788 00444444 00666666 00333333
+
00FFF7FF 00A4E4FF 00FFF6C6 00539373
+
}}
+

          
+

          
+

          
+

          
+
**バイト列のコピー
+
Bufferクラスでは、配列に対して``memcpy``や``memmove``に相当する操作を行うための[[Buffer.BlockCopy>#Buffer.BlockCopy]]・[[Buffer.MemoryCopy>#Buffer.MemoryCopy]]といったメソッドが用意されています。 これらのメソッドを使うと、配列を単なるバイト列やメモリブロックとして扱い、配列の型によらずバイト単位でのコピーを行うことができます。
+

          
+
***BlockCopyメソッド [#Buffer.BlockCopy]
+
&msdn(netfx,member,System.Buffer.BlockCopy){Buffer.BlockCopyメソッド};は、配列をバイト列とみなしてその内容を別の配列へとコピーします。 ``memcpy``や``memmove``に相当する操作は、このBlockCopyメソッドか、後述の[[MemoryCopyメソッド>#Buffer.MemoryCopy]]を使うことで実現できます。
+

          
+
配列をコピーするという点ではBuffer.BlockCopyメソッドと[[Array.Copyメソッド>programming/netfx/arrays/2_operations#Array.Copy]]は似ていますが、BlockCopyメソッドは''要素単位ではなくバイト単位でのコピー''を行います。 引数の指定順序も同じですが、各引数の意味は以下のように異なるため、Array.CopyメソッドとBuffer.BlockCopyメソッドを単純に置き換えて使うことはできません。
+

          
+
|*Buffer.BlockCopyとArray.Copyにおける引数の意味の違い
+
|~メソッド呼び出し|~メソッドの動作|h
+
|``Buffer.BlockCopy(source, 0, dest, 2, 6)``|配列&var{source};の''オフセット''0から6''バイト''分を配列&var{dest};の''オフセット''2以降にコピーする|
+
|``Array.Copy(source, 0, dest, 2, 6)``|配列&var{source};の''インデックス''0から6''要素''分を配列&var{dest};の''インデックス''2以降にコピーする|
+

          
+
#code(cs,Buffer.BlockCopyメソッドを使ってuint配列の一部をバイト列として他の配列にコピーする){{
+
using System;
+
using System.Linq;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    uint[] source = {0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f};
+
    uint[] dest   = {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
+

          
+
    // (配列の内容を16進数で表示)
+
    Console.WriteLine(string.Join(" ", source.Select(e => e.ToString("X8"))));
+
    Console.WriteLine(string.Join(" ", dest.Select(e => e.ToString("X8"))));
+
    Console.WriteLine();
+

          
+
    // 配列sourceのオフセット0から6バイト分を配列destのオフセット2以降にコピーする
+
    Buffer.BlockCopy(source, 0, dest, 2, 6);
+

          
+
    Console.WriteLine(string.Join(" ", dest.Select(e => e.ToString("X8"))));
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
00010203 04050607 08090A0B 0C0D0E0F
+
FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
+

          
+
0203FFFF 06070001 FFFFFFFF FFFFFFFF
+
}}
+

          
+
#remarks
+
上記の例において、配列内の各値は''リトルエンディアン''で格納されている点に注意してください。 配列&var{source};と&var{dest};の内容を図式化すると次にようになります。
+

          
+
#style{{
+
.cell-em {
+
font-weight: bold;
+
color: #c00;
+
}
+
}}
+

          
+
#matrix
+
|*コピー元配列&var{source};の内容
+
|~オフセット(バイト)|~0|~1|~2|~3|~4|~5|~6|~7|~8|~9|~10|~11|~12|~13|~14|~15|
+
|~要素値|>|>|>|CENTER:0x00010203|>|>|>|CENTER:0x04050607|>|>|>|CENTER:0x08090A0B|>|>|>|CENTER:0x0C0D0E0F|
+
|~バイト値|&span(class=cell-em){0x03};|&span(class=cell-em){0x02};|&span(class=cell-em){0x01};|&span(class=cell-em){0x00};|&span(class=cell-em){0x07};|&span(class=cell-em){0x06};|0x05|0x04|0x0B|0x0A|0x09|0x08|0x0F|0x0E|0x0D|0x0C|
+
#matrix-end
+

          
+
#matrix
+
|*コピー後の配列&var{dest};の内容
+
|~オフセット(バイト)|~0|~1|~2|~3|~4|~5|~6|~7|~8|~9|~10|~11|~12|~13|~14|~15|
+
|~要素値|>|>|>|CENTER:0x0203FFFF|>|>|>|CENTER:0x06070001|>|>|>|CENTER:0xFFFFFFFF|>|>|>|CENTER:0xFFFFFFFF|
+
|~バイト値|0xFF|0xFF|&span(class=cell-em){0x03};|&span(class=cell-em){0x02};|&span(class=cell-em){0x01};|&span(class=cell-em){0x00};|&span(class=cell-em){0x07};|&span(class=cell-em){0x06};|0xFF|0xFF|0xFF|0xFF|0xFF|0xFF|0xFF|0xFF|
+
#matrix-end
+

          
+
#remarks-end
+

          
+
このように、BlockCopyメソッドではコピー元・コピー先をバイト単位で指定することができるため、''要素をまたがる位置からの/へのコピー''をすることもできます。
+

          
+

          
+

          
+

          
+

          
+
さらにBlockCopyメソッドでは、コピー先がプリミティブ型配列であれば''異なる型の配列へのコピー''をすることもできます。 そのため、BlockCopyメソッドを使うことによって配列のバイト単位での内容を維持したまま別の型の配列へと変換することができます。
+

          
+
#code(cs,BlockCopyメソッドを使ってバイト列を維持したままuint[]からbyte[]へ変換する){{
+
using System;
+
using System.Linq;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    uint[] source = {0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f};
+
    byte[] dest = new byte[16];
+

          
+
    // (配列の内容を16進数で表示)
+
    Console.WriteLine(string.Join(" ", source.Select(e => e.ToString("X8"))));
+
    Console.WriteLine(string.Join(" ", dest.Select(e => e.ToString("X2"))));
+
    Console.WriteLine();
+

          
+
    // 配列sourceのオフセット0から16バイト分を配列destのオフセット0以降にコピーする
+
    // (バイト単位でコピーしてuint[]からbyte[]へ型変換する)
+
    Buffer.BlockCopy(source, 0, dest, 0, 16);
+

          
+
    Console.WriteLine(string.Join(" ", dest.Select(e => e.ToString("X2"))));
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
00010203 04050607 08090A0B 0C0D0E0F
+
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+

          
+
03 02 01 00 07 06 05 04 0B 0A 09 08 0F 0E 0D 0C
+
}}
+

          
+
この例のように、BlockCopyメソッドでは``uint``→``byte``のように型としては互換性のない変換(''縮小変換''となる変換)でもコピーを行うことができます。 Array.Copyメソッドでは、``uint[]``→``byte[]``へのコピーのように縮小変換を伴うコピーを行おうとすると例外&msdn(netfx,type,System.ArrayTypeMismatchException){ArrayTypeMismatchException};がスローされます。
+

          
+
#remarks
+
型の変換の種類、''縮小変換''については[[programming/netfx/conversion/0_basetype]]を参照してください。
+
#remarks-end
+

          
+

          
+

          
+
BlockCopyメソッドでは、コピー元とコピー先の領域がオーバーラップしていても(重なった状態でも)、適切にコピーが行われます。 オーバーラップした部分のコピー中の状態がコピー先に反映されることはありません。 (この動作は、Array.Copyメソッドでも同様です)
+

          
+
#code(cs,Buffer.BlockCopyメソッドでコピー範囲がオーバーラップするコピーを行う){{
+
using System;
+
using System.Linq;
+
using System.Text;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    byte[] bytes = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+

          
+
    Console.WriteLine(string.Join(" ", bytes.Select(e => e.ToString("X2"))));
+

          
+
    // bytesのオフセット0以降にbytesのオフセット2以降から4バイト分をコピーする
+
    // (コピー元とコピー先の範囲がオーバーラップしていても、コピー途中の状態が反映されることはない)
+
    Buffer.BlockCopy(bytes, 0, bytes, 2, 4);
+

          
+
    // Array.Copyメソッドでも同じ動作となる
+
    //Array.Copy(bytes, 0, bytes, 2, 4);
+

          
+
    Console.WriteLine(string.Join(" ", bytes.Select(e => e.ToString("X2"))));
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
00 01 02 03 04 05 06 07
+
00 01 00 01 02 03 06 07
+
}}
+

          
+

          
+

          
+
Buffer.BlockCopyメソッドと[[Array.Copyメソッド>programming/netfx/arrays/2_operations#Array.Copy]]の相違点を整理すると次のようになります。
+

          
+
|*Buffer.BlockCopyとArray.Copyの相違点
+
|~操作|~Buffer.BlockCopy|~Array.Copy|h
+
|~コピーの単位|バイト単位|要素単位|
+
|~要素をまたがる位置からの/へのコピー|可|不可|
+
|~異なる型の配列へのコピー|プリミティブ型であれば可 (``byte``⇄``int``,``int``⇄``double``など)&br;それ以外の型の場合は&msdn(netfx,type,System.ArgumentException){ArgumentException};|プリミティブ型かつ型に互換性がある場合のみ可 (``byte``→``int``,``int``→``double``など)&br;それ以外の型の場合は&msdn(netfx,type,System.ArrayTypeMismatchException){ArrayTypeMismatchException};&br;(詳細:[[programming/netfx/arrays/2_operations#copy_to_array_of_different_type]])|
+
|~オーバーラップする範囲のコピー|適切にコピーされる|適切にコピーされる|
+

          
+
#remarks
+
BlockCopyメソッド以外でメモリブロックのコピーを行う方法、またそのパフォーマンスについては[[#copy_memory_block]]で別途解説します。
+
#remarks-end
+

          
+

          
+

          
+
***MemoryCopyメソッド [#Buffer.MemoryCopy]
+
&msdn(netfx,member,System.Buffer.MemoryCopy){Buffer.MemoryCopy};は.NET Framework 4.6より利用可能になったメソッドで、名前が示すとおり``memcpy``に相当するメソッドです(厳密には、よりセキュアな関数``memcpy_s``に近い)。
+

          
+
MemoryCopyメソッドは[[BlockCopyメソッド>#Buffer.BlockCopy]]とは異なり、コピー元・コピー先の配列をポインタで指定します。 MemoryCopyメソッドでは、バッファオーバーランを起こす可能性を低減するために、まず引数&var{destinationSizeInBytes};にコピー先の使用可能なサイズを指定します。 その上で、引数&var{sourceBytesToCopy};にコピーするバイト数を指定します。
+

          
+
MemoryCopyメソッドではコピー元・コピー先の配列とオフセットをポインタとして指定するため、``unsafe``コンテキストで用いる必要があります。
+

          
+
#code(cs,MemoryCopyメソッドを使ってバイト列を維持したままuint[]からbyte[]へ変換する){{
+
using System;
+
using System.Linq;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    uint[] source = {0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f};
+
    byte[] dest = new byte[16];
+

          
+
    // (配列の内容を16進数で表示)
+
    Console.WriteLine(string.Join(" ", source.Select(e => e.ToString("X8"))));
+
    Console.WriteLine(string.Join(" ", dest.Select(e => e.ToString("X2"))));
+
    Console.WriteLine();
+

          
+
    unsafe {
+
      // 配列のポインタを取得する
+
      fixed (void* ptrSource = source, ptrDest = dest) {
+
        // 配列sourceの先頭から16バイト分を配列destの先頭にコピーする
+
        // (バイト単位でコピーしてuint[]からbyte[]へ型変換する)
+
        Buffer.MemoryCopy(ptrSource, ptrDest, Buffer.ByteLength(dest), 16);
+
      }
+
    }
+

          
+
    Console.WriteLine(string.Join(" ", dest.Select(e => e.ToString("X2"))));
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
00010203 04050607 08090A0B 0C0D0E0F
+
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+

          
+
03 02 01 00 07 06 05 04 0B 0A 09 08 0F 0E 0D 0C
+
}}
+

          
+
#remarks
+
配列⇄アンマネージポインタ間でのコピーを行う場合は、&msdn(netfx,member,System.Runtime.InteropServices.Marshal.Copy){Marshal.Copyメソッド};を使うことができます。
+
#remarks-end
+

          
+
#remarks
+
MemoryBlockメソッド以外でメモリブロックのコピーを行う方法、またそのパフォーマンスについては[[#copy_memory_block]]で別途解説します。
+
#remarks-end
+

          
+

          
+
*バイト列操作に関するクラス
+
ここではBufferクラス以外でバイト列操作に関連するクラスにどのようなものがあるか紹介します。
+

          
+
**プリミティブ型とバイト列の相互変換 (BitConverter) [#System.BitConverter]
+
Bufferクラスのメソッドではプリミティブ型配列の操作を行うことができますが、プリミティブ型の値そのものをバイト列として扱うことはできません。 プリミティブ型の値のバイト列を取得する、また逆にバイト列からプリミティブ型の値に変換する操作を行うには&msdn(netfx,type,System.BitConverter){BitConverterクラス};を使います。
+

          
+
バイト列への変換には&msdn(netfx,member,System.BitConverter.GetBytes){BitConverter.GetBytesメソッド};、バイト列からの変換には&msdn(netfx,member,System.BitConverter.ToInt32){BitConverter.ToInt32};などのメソッドを使います。
+

          
+
#code(cs,BitConverterクラスを使ってプリミティブ型の値とバイト列を相互に変換する){{
+
using System;
+
using System.Linq;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    // int型の値をバイト列に変換する
+
    int val = 0x01234567;
+
    byte[] bytes = BitConverter.GetBytes(val);
+

          
+
    Console.WriteLine("{0:X8} -> {1}", val, string.Join(" ", bytes.Select(e => e.ToString("X2"))));
+

          
+
    // バイト列のオフセット0から4バイト分をint型(Int32)の値に変換する
+
    bytes = new byte[] {0xde, 0xad, 0xbe, 0xef};
+
    val = BitConverter.ToInt32(bytes, 0);
+

          
+
    Console.WriteLine("{0} -> {1:X8}", string.Join(" ", bytes.Select(e => e.ToString("X2"))), val);
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
01234567 -> 67 45 23 01
+
DE AD BE EF -> EFBEADDE
+
}}
+

          
+
#remarks
+
プリミティブ型⇄バイト列の相互変換、BitConverterクラスについては[[programming/netfx/conversion/0_basetype#ByteArrayConversion_BitConverter]]を参照してください。
+
#remarks-end
+

          
+

          
+
**文字列とバイト列の相互変換 (Encoding) [#System.Text.Encoding]
+
文字列をバイト列として扱うには&msdn(netfx,type,System.Text.Encoding){Encodingクラス};を使います。 Encodingクラスは特定の文字コードにおける変換規則を表すクラスです。
+

          
+
バイト列への変換には&msdn(netfx,member,System.Text.Encoding.GetBytes){Encoding.GetBytesメソッド};、バイト列からの変換には&msdn(netfx,member,System.Text.Encoding.GetString){Encoding.GetStringメソッド};を使います。
+

          
+
#code(cs,BitConverterクラスを使って文字列とバイト列を相互に変換する){{
+
using System;
+
using System.Linq;
+
using System.Text;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    // 文字列をUTF-8のバイト列に変換する
+
    string str = "日本語";
+
    byte[] bytes = Encoding.UTF8.GetBytes(str);
+

          
+
    Console.WriteLine("{0} -> {1}", str, string.Join(" ", bytes.Select(e => e.ToString("X2"))));
+

          
+
    // バイト列をShift_JISの文字列とみなして変換する
+
    bytes = new byte[] {0x8A, 0xBF, 0x8E, 0x9A};
+
    str = Encoding.GetEncoding("Shift_JIS").GetString(bytes);
+

          
+
    Console.WriteLine("{0} -> {1}", string.Join(" ", bytes.Select(e => e.ToString("X2"))), str);
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
日本語 -> E6 97 A5 E6 9C AC E8 AA 9E
+
8A BF 8E 9A -> 漢字
+
}}
+

          
+
#relevantdocs
+

          
+
-[[programming/netfx/filesystem/2_filereadwrite#Encoding.GetEncoding]]
+
-[[programming/netfx/stream/2_streamreader_streamwriter#StreamReaderStreamWriter_Encoding]]
+
-[[programming/netfx/conversion/0_basetype#ByteArrayConversion_Encoding]]
+

          
+
#relevantdocs-end
+

          
+

          
+
**構造体とバイト列の相互変換
+
.NET Frameworkでは任意の構造体とバイト列を直接相互に変換するクラスやメソッドが用意されていません。 独自に実装する方法については[[programming/netfx/tips/convert_struct_and_bytearray]]を参照してください。
+

          
+

          
+
**バイト列のハッシュ化・暗号化・フォーマット変換
+
バイト列をMD5, SHA-1, SHA-512などのハッシュ関数でハッシュ化する方法、暗号化する方法、BASE64などにフォーマット変換する方法については[[programming/netfx/text_format_conversion]]を参照してください。
+

          
+

          
+
**MemoryStreamとBinaryReader/BinaryWriterを使ったバイト列の相互変換
+
MemoryStreamとBinaryReader/BinaryWriterを組み合わせて使うことによっても、プリミティブ型とバイト列の相互変換をすることができます。
+

          
+
#code(cs,MemoryStreamとBinaryWriterを使ってuint型の値をバイト列に変換する){{
+
using System;
+
using System.IO;
+
using System.Linq;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    // 可変長のバッファを確保するMemoryStreamを作成
+
    using (var stream = new MemoryStream()) {
+
      // MemoryStreamに書き込むBinaryWriterを作成
+
      using (var writer = new BinaryWriter(stream)) {
+
        // uint型の値を書き込む
+
        writer.Write((uint)0x01234567);
+
        writer.Write((uint)0x89abcdef);
+
      }
+

          
+
      // MemoryStreamに書き込んだ内容をbyte型の配列に変換
+
      var bytes = stream.ToArray();
+

          
+
      Console.WriteLine(string.Join(" ", bytes.Select(e => e.ToString("X2"))));
+
    }
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
67 45 23 01 EF CD AB 89
+
}}
+

          
+

          
+

          
+

          
+
#code(cs,MemoryStreamとBinaryReaderを使ってバイト列をuint型の値に変換する){{
+
using System;
+
using System.IO;
+
using System.Linq;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    byte[] bytes = new byte[] {0x67, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x89};
+

          
+
    // 既存の固定長バッファから読み込むMemoryStreamを作成
+
    using (var stream = new MemoryStream(bytes, false)) {
+
      // MemoryStreamから読み込むBinaryReaderを作成
+
      using (var reader = new BinaryReader(stream)) {
+
        // uint型の値を読み込む
+
        Console.WriteLine("{0:X8}", reader.ReadUInt32());
+
        Console.WriteLine("{0:X8}", reader.ReadUInt32());
+
      }
+
    }
+
  }
+
}
+
}}
+

          
+

          
+
#prompt(実行結果){{
+
01234567
+
89ABCDEF
+
}}
+

          
+
#remarks
+
MemoryStreamについての詳細は[[programming/netfx/stream/1_2_memorystream]]、BinaryReader/BinaryWriterについては[[programming/netfx/stream/3_binaryreader_binarywriter]]を参照してください。
+
#remarks-end
+

          
+

          
+

          
+
*メモリブロックのコピー手法とパフォーマンス [#copy_memory_block]
+
.NET Frameworkのクラスライブラリで用いることができるメソッドを使って、``memcpy``のようなメモリブロックのコピーを行う方法と、そのパフォーマンスについて比較します。 ここでは下記メソッドを検証の対象としています。
+

          
+
-&msdn(netfx,member,System.Array.Copy);
+
-&msdn(netfx,member,System.Buffer.BlockCopy);
+
-&msdn(netfx,member,System.Buffer.MemoryCopy);
+
-&msdn(netfx,member,System.Runtime.InteropServices.Marshal.Copy);
+

          
+
以下は1kBのバッファを10,000,000回コピーした場合と、1MBのバッファを10,000回コピーした場合における各メソッドの所要時間です。 ほぼ同等のスペックではあるものの、それぞれ異なる構成の環境での結果になります。(ランタイムと構成の詳細は後に掲載)
+

          
+
#column
+
#prompt(Mono master on Ubuntu 14.04){{
+
$ mcs /unsafe test.cs && mono test.exe
+
Unix 3.13.0.24
+
4.6.57.0
+
repeat: 10,000,000
+
bufferSize: 1,024
+
[trial0]
+
Array.Copy          : 00:00:01.6362623
+
Buffer.BlockCopy    : 00:00:00.9508335
+
Buffer.MemoryCopy   : 00:00:04.8978061
+
Marshal.Copy        : 00:00:01.0364196
+
[trial1]
+
Array.Copy          : 00:00:01.5339539
+
Buffer.BlockCopy    : 00:00:00.9387640
+
Buffer.MemoryCopy   : 00:00:04.7937147
+
Marshal.Copy        : 00:00:01.0056053
+
[trial2]
+
Array.Copy          : 00:00:01.5417680
+
Buffer.BlockCopy    : 00:00:00.9607770
+
Buffer.MemoryCopy   : 00:00:04.8224396
+
Marshal.Copy        : 00:00:01.0089125
+

          
+
$ mcs /unsafe test.cs && mono test.exe
+
Unix 3.13.0.24
+
4.6.57.0
+
repeat: 10,000
+
bufferSize: 1,048,576
+
[trial0]
+
Array.Copy          : 00:00:01.2085023
+
Buffer.BlockCopy    : 00:00:01.7494556
+
Buffer.MemoryCopy   : 00:00:05.4265505
+
Marshal.Copy        : 00:00:01.7614601
+
[trial1]
+
Array.Copy          : 00:00:01.2344488
+
Buffer.BlockCopy    : 00:00:01.7310019
+
Buffer.MemoryCopy   : 00:00:05.2115303
+
Marshal.Copy        : 00:00:01.7434765
+
[trial2]
+
Array.Copy          : 00:00:01.2652210
+
Buffer.BlockCopy    : 00:00:01.7775290
+
Buffer.MemoryCopy   : 00:00:05.3989120
+
Marshal.Copy        : 00:00:01.7415971
+
}}
+
#column
+
#prompt(.NET Framework 4.6 on Windows 7){{
+
> csc /unsafe test.cs && test.exe
+
Microsoft Windows NT 6.1.7601 Service Pack 1
+
4.0.30319.42000
+
repeat: 10,000,000
+
bufferSize: 1,024
+
[trial0]
+
Array.Copy          : 00:00:01.0222218
+
Buffer.BlockCopy    : 00:00:01.0212767
+
Buffer.MemoryCopy   : 00:00:00.9842077
+
Marshal.Copy        : 00:00:01.0883042
+
[trial1]
+
Array.Copy          : 00:00:01.0779283
+
Buffer.BlockCopy    : 00:00:01.0214242
+
Buffer.MemoryCopy   : 00:00:00.9880428
+
Marshal.Copy        : 00:00:01.1224393
+
[trial2]
+
Array.Copy          : 00:00:01.0340761
+
Buffer.BlockCopy    : 00:00:01.0217731
+
Buffer.MemoryCopy   : 00:00:00.9855210
+
Marshal.Copy        : 00:00:01.0926419
+

          
+
> csc /unsafe test.cs && test.exe
+
Microsoft Windows NT 6.1.7601 Service Pack 1
+
4.0.30319.42000
+
repeat: 10,000
+
bufferSize: 1,048,576
+
[trial0]
+
Array.Copy          : 00:00:11.1120259
+
Buffer.BlockCopy    : 00:00:11.1659976
+
Buffer.MemoryCopy   : 00:00:10.9877895
+
Marshal.Copy        : 00:00:11.2638044
+
[trial1]
+
Array.Copy          : 00:00:11.0481049
+
Buffer.BlockCopy    : 00:00:11.0484399
+
Buffer.MemoryCopy   : 00:00:10.9999365
+
Marshal.Copy        : 00:00:10.9938771
+
[trial2]
+
Array.Copy          : 00:00:10.9910499
+
Buffer.BlockCopy    : 00:00:11.0081194
+
Buffer.MemoryCopy   : 00:00:10.9891173
+
Marshal.Copy        : 00:00:10.9937553
+
}}
+
#column-end
+

          
+
-結果
+
--.NET Frameworkでは、どのメソッドでもほぼ同等の速度
+
--Monoでは、(1)Array.Copy、(2)Buffer.BlockCopyとMarshal.Copy、(3)Buffer.MemoryCopyで速度が異なる(=それぞれ異なる実装になっている?)
+
--Monoでは、Buffer.MemoryCopyは際立ってパフォーマンスが悪い
+
--Monoではバッファのサイズが大きくなってもさほど変わらない、.NET Frameworkではバッファのサイズが大きくなると遅くなる
+

          
+
この結果を見る限りでは、Buffer.MemoryCopyやMarshal.Copyによるコピーに大きな優位はないようです。 ただ、マルチプラットフォームでの動作を想定したコードでBuffer.MemoryCopyを使用する場合は、ほかのメソッドを使ったほうがよいか検討する余地がありそうです。
+

          
+
#code(cs,type=benchmark,検証に使ったコード){{
+
using System;
+
using System.IO;
+
using System.Runtime.InteropServices;
+
using System.Diagnostics;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    Console.WriteLine(Environment.OSVersion);
+
    Console.WriteLine(Environment.Version);
+

          
+
    const int trial = 3; // 各テストを3回試行
+
    const int repeat = 10 * 1000; // 各テストにおけるコピーの繰り返し回数
+
    const int bufferSize = 1024 * 1024; // コピーするバッファのサイズ
+

          
+
    var bufferSource = new byte[bufferSize];
+
    var bufferDest   = new byte[bufferSize];
+

          
+
    Console.WriteLine("repeat: {0:N0}", repeat);
+
    Console.WriteLine("bufferSize: {0:N0}", bufferSize);
+

          
+
    for (var t = 0; t < trial; t++) {
+
      Console.WriteLine("[trial{0}]", t);
+

          
+
      // Array.Copyメソッドによるコピー
+
      {
+
        var lengthToCopy = bufferSource.Length;
+
        var sw = Stopwatch.StartNew();
+

          
+
        for (var r = 0; r < repeat; r++) {
+
          Array.Copy(bufferSource, 0,
+
                     bufferDest, 0, lengthToCopy);
+
        }
+

          
+
        Console.WriteLine("{0,-20}: {1}", "Array.Copy", sw.Elapsed);
+
      }
+

          
+
      // Buffer.BlockCopyメソッドによるコピー
+
      {
+
        var bytesToCopy = Buffer.ByteLength(bufferSource);
+
        var sw = Stopwatch.StartNew();
+

          
+
        for (var r = 0; r < repeat; r++) {
+
          Buffer.BlockCopy(bufferSource, 0,
+
                           bufferDest, 0, bytesToCopy);
+
        }
+

          
+
        Console.WriteLine("{0,-20}: {1}", "Buffer.BlockCopy", sw.Elapsed);
+
      }
+

          
+
      // Buffer.MemoryCopyメソッドによるコピー
+
      {
+
        unsafe {
+
          fixed (void* source = bufferSource, dest = bufferDest) {
+
            var bytesToCopy = Buffer.ByteLength(bufferSource);
+
            var destSizeInBytes = sizeof(byte) * bufferDest.Length;
+
            var sw = Stopwatch.StartNew();
+

          
+
            for (var r = 0; r < repeat; r++) {
+
              Buffer.MemoryCopy(source, dest, destSizeInBytes, bytesToCopy);
+
            }
+

          
+
            Console.WriteLine("{0,-20}: {1}", "Buffer.MemoryCopy", sw.Elapsed);
+
          }
+
        }
+
      }
+

          
+
      // Marshal.Copyメソッドによるコピー
+
      {
+
        unsafe {
+
          fixed (void* dest = bufferDest) {
+
            var lengthToCopy = bufferSource.Length;
+
            var ptrDest = new IntPtr(dest);
+
            var sw = Stopwatch.StartNew();
+

          
+
            for (var r = 0; r < repeat; r++) {
+
              Marshal.Copy(bufferSource, 0, ptrDest, lengthToCopy);
+
            }
+

          
+
            Console.WriteLine("{0,-20}: {1}", "Marshal.Copy", sw.Elapsed);
+
          }
+
        }
+
      }
+

          
+
#if false
+
      // Stream.CopyToメソッドによるコピー
+
      {
+
        var sw = Stopwatch.StartNew();
+

          
+
        for (var r = 0; r < repeat; r++) {
+
          using (var sourceStream = new MemoryStream(bufferSource, false))
+
          using (var destStream   = new MemoryStream(bufferDest,   true)) {
+
            sourceStream.CopyTo(destStream, 1024);
+
          }
+
        }
+

          
+
        Console.WriteLine("{0,-20}: {1}", "Stream.CopyTo", sw.Elapsed);
+
      }
+
#endif
+
    }
+
  }
+
}
+
}}
+

          
+
#remarks
+
このコードには&msdn(netfx,member,System.IO.Stream.CopyTo);によるコピーを掲載しています。 ただ、引数に指定するバッファサイズ次第でパフォーマンスが大きく変わるため、検証結果からは除外しています。
+
#remarks-end
+

          
+
#prompt(実行環境(Mono master on Ubuntu 14.04)){{
+
$ uname -a
+
Linux smdn.jp 3.13.0-24-generic #47-Ubuntu SMP Fri May 2 23:30:00 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
+

          
+
$ mono -V
+
Mono JIT compiler version 4.3.2 (master/81e1e07 2016年  1月  2日 土曜日 14:47:13 JST)
+
Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
+
	TLS:           __thread
+
	SIGSEGV:       altstack
+
	Notifications: epoll
+
	Architecture:  amd64
+
	Disabled:      none
+
	Misc:          softdebug 
+
	LLVM:          yes(3.6.0svn-mono-master/a173357)
+
	GC:            sgen
+

          
+
& cat /proc/cpuinfo | grep name
+
model name	: Intel(R) Core(TM)2 CPU          6600  @ 2.40GHz
+
model name	: Intel(R) Core(TM)2 CPU          6600  @ 2.40GHz
+
}}
+

          
+
#prompt(実行環境(.NET Framework 4.6 on Windows 7)){{
+
> systeminfo
+
OS 名:                  Microsoft Windows 7 Ultimate 
+
OS バージョン:          6.1.7601 Service Pack 1 ビルド 7601
+
OS 製造元:              Microsoft Corporation
+
OS 構成:                スタンドアロン ワークステーション
+
OS ビルドの種類:        Multiprocessor Free
+
システムの種類:         x64-based PC
+
プロセッサ:             1 プロセッサインストール済みです。
+
                        [01]: AMD64 Family 15 Model 43 Stepping 1 AuthenticAMD ~2200 Mhz
+
}}
+

          
+

          
+
#navi(..)
+

          
+

          
+