Tuesday, July 29, 2008 3:09 PM
Looking around the web, developers often need to cast (map) a byte array into some sort of a structure. In C++, it is as easy as casting the byte array pointer to your structure pointer, however in the managed world of .NET things are different and you can not do this anymore. Let us visit the different approaches developers use to solve this problem.
Some developers choose to write a helper class that makes use of BinaryReader to read values from the binary array depending on the structure they have and write them directly into members of the structure. This method is fast but needs a lot of maintenance since you need to code a class or a method for each structure you have and you need to update it if your structure changes. This solution uses fully managed code.
Other developers choose to use unsafe code and cast the byte array directly into the memory of the structure. The structure should be packed on 1 byte boundary and it should have a sequential layout. And you can not cast your byte array into a class using this method. This method is very fast however it uses unsafe code which is not encouraged because your CLR can not do proper memory checks on this structure anymore. Also since this is not a managed solution you can not port it to VB.NET, as far as I know you can only use unsafe blocks in C#.
Another solution involves getting the help of the Marshal class to cast your structure. However this solution involves copying data between managed and unmanaged heap which is slow and consumes resources, however this solution works on VB.NET and not just C#.
Some developers choose to use reflection code. This solution is similar to the first solution (BinaryReader) but instead of coding a method for each structure you want, reflection is used to automate things and make things "more dynamic". Using this method, instead of coding the actual read operations from the stream into your structure members at compile time, this is decided at runtime by using reflection to detect the members of your structure. This solution saves you a lot of donkey work, unfortunately reflection is slow, very slow compared to normal code. Depending on your application, you might not care much if your code is slow to cast the byte array into your corresponding structure, but if speed is essential, (for example in a network analysis tool) then you need a faster solution than this.
A quick Google search revealed a lot of articles on the subject. I have chosen this article that explains these methods with more details:
http://www.dotnetjunkies.ddj.com/Article/DF6E5BE5-BCB6-43BB-BEE4-1013A2E3D085.dcik
Today I am going to get the help of my friend Mr. Emit, the virtual developer who works for me for free. Using Emit namespace, I will be able to write a solution that is as fast as the first solution (manually coding the read operations of BinaryReader into the members of a structure) and as dynamic as the reflection solution, and the solution will be using fully manageable code and does not involve copying of memory between managed and unmanaged heaps. The solution mostly resembles the first solution but without any of the donkey work, it is fully dynamic.
My solution takes a structure at runtime and uses reflection to detect its members (fields or properties). After detecting the members, the Emit namespace is used to write IL code and create a new class at runtime that uses the BinaryReader approach to read data from the byte array into the structure's members. Unlike using simple reflection code (which has to use reflection every time you want to cast a byte array to any structure), I use reflection once to write a class at runtime that does the casting without reflection. Of course the new dynamically written class is cashed for subsequent castings of the same structure type.
How to use the code:
Using the code is very simple as it is shown in the sample application. Casting involves simple lines of code like this:
byte[] data = new byte[] { 0, 0, 0, 1, 0, 2, 3, 4, 5, 6, 7 };
SampleStructure stru = new SampleStructure();
stru = (SampleStructure)CDynamicCastHelper.CastIntoFields(stru, data,
CastOptions.ReverseDWord | CastOptions.ReverseWord);
SampleClass clas = new SampleClass();
CDynamicCastHelper.CastIntoFields(clas, data,
CastOptions.ReverseDWord | CastOptions.ReverseWord);
You have two methods to use in the class CDynamicCastHelper, CastIntoFields and CastIntoProperties. The first one casts the byte array into fields and the second one casts the byte array into properties. The methods can take either a structure or a class and they take CastOptions enum. CastOptions can reverse 16bit (WORD) and 32bit (DWORD) values for you while being read. If your structure or class has a byte array member that you want to cast data into it, then you must add a special attribute to your byte array member that specifies the size that the CDynamicCastHelper will fill into this byte array member.
If someone can volunteer to port this to VB.NET that would be great, please let me know.
Hope this helps. Let me know if you have any comments.