Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copying byte array to various fields in class/struct in C#

In the example C# code below, I have a byte array which has been read from a socket. I want to parse the data into the various fields of 'exampleClass' (first 8 bytes into the 64-bit variable 'field1', next 4 bytes into 32-bit variable 'field2', etc.)

using System;
namespace CsByteCopy
{
  class Program
  {
    class ExampleClass
    {
      public UInt64 field1;
      public UInt32 field2;
      public UInt16 field3;
      public byte[] field4 = new byte[18];
    }

    static void Main(string[] args)
    {
      byte[] exampleData =
      {
        // These 8 bytes should go in 'field1'
        0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
        // These 4 bytes should go in 'field2'
        0x08,0x09,0x0A,0x0B,
        // These 2 bytes should go in 'field3'
        0x0C,0x0D,
        // These 18 * 1 bytes should go in 'field4'
        0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
      };

      ExampleClass exampleClass = new ExampleClass();
      // Perform copy
    }
  }
}

It's been a long time since I last used C, but if I recall correctly, I might have been able to get away with a single memcpy() call to populate all the fields in the class. What's the most efficient way of populating the fields of 'exampleClass' in C#?

like image 430
JamesPD Avatar asked Aug 11 '11 17:08

JamesPD


1 Answers

You have a lot of options, the one that turns out the best usually depends on what your program needs/can handle and exactly how much speed you want. There are a lot of articles that explain the different ways you can populate a class or a struct with data.

Binary Serialization is one way. That requires you write a custom serializer, which is fairly easy. An MSDN article regarding that shows it's not too difficult.

You can write a private constructor that takes in the byte array and uses a BinaryReader or the BitConverter class to read from the byte array (you'd have to wrap it in a MemoryStream for BinaryReader) or to simply convert sections of the byte array to the values you need (BitConverter). In the case of BitConverter you would also need to use Buffer.BlockCopy to copy the remaining data of the byte array to the byte array field in your class

A third way, also generally the fastest way, is to convert your class to a struct and use unsafe code to cast the byte array as that struct. Something like this:

unsafe struct ExampleClass
{
   public ulong field1;
   public uint field2
   public ushort field3
   public fixed byte field4[18];

   public static ExampleClass ReadStruct(byte[] data)
   {
       fixed (byte* pb = &data[0])
       {
           return *(ExampleClass*)pb;
       }
   }
}

Of course, the above code is only valid if you can use unsafe code. Furthermore, converting the class to a struct may also not be what you're looking for. In most cases, structs are passed by value to functions, so that calling methods copies the entire struct (in this case 32 bytes) instead of passing a reference (4 or 8 bytes, depending on CPU architecture) and can reduce the efficiency of your program. There are exceptions to structs being passed by value, and you can use your favorite search engine for that.

If you can't use unsafe code, there is also Marshal.PtrToStructure which will do the same as the above code, but about 10 times slower. You would also need to use the MarshalAs attribute to specify the size of the array, instead of using the fixed keyword (which is unsafe). At that point, you might as well use the BinaryReader/BitConverter, as it will be faster than the marshal class.

like image 158
Christopher Currens Avatar answered Nov 06 '22 22:11

Christopher Currens