Buffer
The C# Buffer
type handles ranges of bytes. It includes the optimized Buffer.BlockCopy
method—this copies a range of bytes from one array to another.
Buffer
methodsThe Buffer
class
also provides the ByteLength
, GetByte
and SetByte
methods. Usually BlockCopy
is the most interesting.
BlockCopy
To begin, Buffer.BlockCopy
does not copy logical elements in arrays. Instead it copies bytes. We call it to copy the bytes in one byte
array to another.
byte
array arr2 is automatically initialized to all zero bytes.using System; class Program { static void Main() { byte[] arr1 = new byte[] { 1, 2, 3, 4, 5 }; byte[] arr2 = new byte[10]; // Copy the first five bytes from arr1 to arr2 Buffer.BlockCopy(arr1, 0, arr2, 0, 5); Display(arr2); } static void Display(byte[] arr) { for (int i = 0; i < arr.Length; i++) { Console.Write(arr[i]); } Console.WriteLine(); } }1234500000
Buffer.BlockCopy
can act on a data type that is not 1 byte
. An int
is 4 bytes. The fifth parameter of Buffer.BlockCopy
is the number of bytes to copy.
int
.sizeof
operator returns the byte
length of a primitive data type like int
. Here it returns 4.int
times 4 can be resolved by the C# compiler—it won't cause any slowdowns.using System; class Program { static void Main() { int[] arr1 = new int[] { 1, 2, 3, 4, 5 }; int[] arr2 = new int[10]; // Copy the first twenty bytes from arr1 to arr2 Buffer.BlockCopy(arr1, 0, arr2, 0, 5 * sizeof(int)); Display(arr2); } static void Display(int[] arr) { for (int i = 0; i < arr.Length; i++) { Console.Write(arr[i]); } Console.WriteLine(); } }1234500000
ByteLength
ByteLength
returns the byte
count for an array. In this example there are 2 arrays—an integer array and a byte
array—allocated on the managed heap.
Buffer.ByteLength
to count the total number of bytes in each array. Each int
is 4 bytes. Each byte
is a single byte
.using System; class Program { static void Main() { // Example arrays for program. // ... Each array has three elements. int[] array1 = { 1, 2, 3 }; byte[] array2 = { 1, 2, 3 }; // Get lengths of arrays. // ... This counts the bytes in all elements. int length1 = Buffer.ByteLength(array1); int length2 = Buffer.ByteLength(array2); // Write results. Console.WriteLine(length1); Console.WriteLine(length2); } }12 3
GetByte
, SetByte
Next we use Buffer.GetByte
and SetByte
. We have an array of 3 integer values (an int
array). Note that integers (int
variables) in C# code are represented by 4 bytes.
GetByte
and SetByte
, we read and assign the individual bytes in each integer directly.using System; class Program { static void Main() { // Use an array of three integers for testing. // ... Loop through the bytes in the array and write their values. int[] array1 = { 1, 1, 256 }; for (int i = 0; i < Buffer.ByteLength(array1); i++) { Console.WriteLine(Buffer.GetByte(array1, i)); } // Set certain byte values at indexes in the array. Buffer.SetByte(array1, 0, 55); Buffer.SetByte(array1, 4, 55); Buffer.SetByte(array1, 8, 55); // Render the modified array. Console.WriteLine("---"); for (int i = 0; i < Buffer.ByteLength(array1); i++) { Console.WriteLine(Buffer.GetByte(array1, i)); } } }1 0 0 0 1 0 0 0 0 1 0 0 --- 55 0 0 0 55 0 0 0 55 1 0 0
BlockCopy
, Array.Copy
To test Buffer.BlockCopy
further, we benchmarked it on a 1000-element array. We compare it with the C# Array.Copy
method.
Buffer.BlockCopy
on a 1000-element byte
array. It tests the result for correctness.Array.Copy
with similar arguments to the Buffer.BlockCopy
version.Buffer.BlockCopy
and Array.Copy
have the same performance on an int
array.using System; using System.Diagnostics; class Program { static byte[] GetSourceArray() { // Populate data. var result = new byte[1000]; result[0] = 100; result[999] = 1; return result; } static bool IsValidData(byte[] data) { // Test data. return data[0] == 100 && data[999] == 1; } const int _max = 10000000; static void Main() { const int size = 1000; byte[] source = GetSourceArray(); byte[] target = new byte[size]; // Version 1: use Buffer.BlockCopy. var s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { Buffer.BlockCopy(source, 0, target, 0, size); if (!IsValidData(target)) { return; } } s1.Stop(); // Reset. source = GetSourceArray(); target = new byte[size]; // Version 2: use Array.Copy. var s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { Array.Copy(source, target, size); if (!IsValidData(target)) { return; } } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); } }23.49 ns Buffer.BlockCopy 24.56 ns Array.Copy
BitConverter
With this class
we can change representations of value types. The BitConverter
is sometimes used alongside Buffer.BlockCopy
in programs that handle low-level data.
The Buffer
type contains low-level methods that act upon bytes. A buffer is a range of bytes. With BlockCopy
, we optimize large array copies of value types.