How to convert an array of value struct to bytes? - c#

I have a System.Array of value struct types, something like this:
public value struct Position
{
int timestamp;
float x;
float y;
}
Position[] positions = new Position[1000 * 1000];
After I initialize the array with values, how can I get a byte[] of it's content, without serializing one item at a time?
In C++/CLI I would use a pin_ptr to get the pointer to the array content and I would copy the data from there. Can I do something like this in C#?
EDIT: I need to write the raw data to disk, as if it were a C struct, without any kind of serialization.
I tagged this question as C# for broader exposure, but actually I'm trying to serialize the data from IronPython, so this means I can't use any unsafe C# functionality.

Maybe this will help:
[Serializable()]
public struct Position
{
int timestamp;
float x;
float y;
}
static void Main(string[] args)
{
var positions = new Position[1000 * 1000];
GetBytes(positions);
}
private static byte[] GetBytes(object obj)
{
using (var memoryStream = new System.IO.MemoryStream())
{
var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
binaryFormatter.Serialize(memoryStream, obj);
return memoryStream.ToArray();
}
}

Here's a method that doesn't require unsafe code:
[Updated to remove the for loop and perform the copy in a single pass]
private static byte[] StructureToByteArray(Position[] posArray)
{
if (posArray == null || posArray.Length == 0)
{
return new byte[0];
}
var lenOfObject = Marshal.SizeOf(typeof(Position));
var len = lenOfObject * posArray.Length;
var arr = new byte[len];
var handle = GCHandle.Alloc(posArray, GCHandleType.Pinned);
try
{
var ptr = handle.AddrOfPinnedObject();
Marshal.Copy(ptr, arr, 0, len);
}
finally
{
handle.Free();
}
return arr;
}

I believe this is the equivalent to the C++/CLI pin_ptr using unsafe C# code:
public static unsafe byte[] GetBytes(Position[] positions)
{
byte[] result = new byte[positions.Length * Marshal.SizeOf(typeof(Position))];
fixed (Position* ptr = &positions[0])
{
Marshal.Copy((IntPtr)ptr, result, 0, result.Length);
}
return result;
}

Related

Packing udp packet of arbitrary number of coordinates

So I am getting a variable number of coordinates (latitude and longitude). I want to pack these coordinates in a straight forward way that can be easily sent via UDP and unpacked. How can I go about this with C#?
I'm assuming I declare a struct and then use the built in marshalling to get a byte array to send. How does one do this when there is a variable number of points involved?
Most of my programming experience has been in Python, but I need this to be done in C#, which I have limited experience with.
edit: I'll add some code I was testing out, since I feel like no one responds to just text.
namespace ConsoleApplication1
{
class Testing
{
static void Main(string[] args)
{
// Console.WriteLine("Starting");
// string text = "Hello";
// byte[] data = Encoding.ASCII.GetBytes(text);
StartPacket test = new StartPacket();
test.len = 3;
List<double> points = new List<double>();
points.Add(3.14);
points.Add(5);
points.Add(-1023.1231311);
test.points = points;
byte[] data = StructureToByteArray(test);
SendUdp(65456, "192.168.20.100", data);
}
static void SendUdp(int srcPort, string dstIp, byte[] data)
{
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
ProtocolType.Udp);
IPAddress dst = IPAddress.Parse(dstIp);
IPEndPoint endPoint = new IPEndPoint(dst, srcPort);
sock.SendTo(data, endPoint);
}
public struct StartPacket
{
public uint len;
public List<double> points;
}
public static byte[] StructureToByteArray(object obj)
{
int len = Marshal.SizeOf(obj);
byte[] arr = new byte[len];
IntPtr ptr = Marshal.AllocHGlobal(len);
Marshal.StructureToPtr(obj, ptr, true);
Marshal.Copy(ptr, arr, 0, len);
Marshal.FreeHGlobal(ptr);
return arr;
}
}
}
This code fails with:
Type 'ConsoleApplication1.Testing+StartPacket' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.
This seems more like a serialization/deserialization task. The simplest approach is to mark the class with Serializable then use the BinaryFormatter like this:
public class Program
{
public static void Main(string[] args)
{
var startPacket = new StartPacket();
startPacket.len = 3;
startPacket.points = new List<double>() { 3.14, 5, -1023.1231311 };
// serialize into a byte array for Socket.SendTo()
var formatter = new BinaryFormatter();
var ms = new MemoryStream();
formatter.Serialize(ms, startPacket);
var bytes = ms.ToArray();
// assuming we received bytes[] from a socket, deserialize into StartPacket object
ms = new MemoryStream(bytes);
formatter = new BinaryFormatter();
startPacket = (StartPacket)formatter.Deserialize(ms);
}
}
[Serializable()]
public struct StartPacket
{
public uint len;
public List<double> points;
}
However, this is not very efficient memory-wise--this example generates a 524-byte serialized StartPacket, probably due to the fact that the List<> will have a capacity of more than 3 doubles. Making points an array with the specific size only gets us down to 211 bytes. Without looking into it, I'd guess that there's a lot of overhead in the serialized version of the struct (like the names and types of variables, etc). However, if you're not too concerned about the size of the packet, then this might work for you.
If you want something more efficient, then you can add methods to StartPacket like so:
public class Program
{
public static void Main(string[] args)
{
var startPacket = new StartPacket();
startPacket.len = 3;
startPacket.points = new List<double> { 3.14, 5, -1023.1231311 };
// create an array to send through the socket
var arr = startPacket.ToArray();
// create a StartPacket from an array we received from a socket
var newStartPacket = StartPacket.FromArray(arr);
}
}
public struct StartPacket
{
public uint len;
public List<double> points;
public byte[] ToArray()
{
var arr = BitConverter.GetBytes(len);
foreach (var point in points)
{
arr = arr.Concat(BitConverter.GetBytes(point)).ToArray();
}
return arr;
}
public static StartPacket FromArray(byte[] array)
{
var sp = new StartPacket();
sp.len = BitConverter.ToUInt32(array, 0);
sp.points = new List<double>();
for (int i = 0; i < sp.len; i++)
{
sp.points.Add(BitConverter.ToDouble(array, 4 + i * 8));
}
return sp;
}
}
Note that neither of these account for the endianness of the system. Hope this helps.

C# copy array of structs to array of doubles

[StructLayout(LayoutKind.Sequential)]
public struct Demo
{
double X;
double Y;
}
var data = new Demo[128];
FillWithMeaningfulValues(data);
double[] doubles;
Copy(data, out doubles); // ?
How do I copy the demo array into the doubles array without having to for(...) through each element? In C++, I would use memcpy, but in C# I did not find what I need in Marshal.Copy.
void MethodIDoNotWantToUse(Demo[] demo, out double[] doubles)
{
doubles = new double[demo.Length * 2];
for(int i = 0, j = 0; i < demo.Length; ++i)
{
doubles[j++] = demo[i].X;
doubles[j++] = demo[i].Y;
}
}
void MethodIWouldPreferToUse(Demo[] demo, out double[] doubles)
{
doubles = new double[demo.Length * 2];
memcopy(doubles, demo, demo.Length * 2 * sizeof(double));
}
You'll do something like this. Marshal.Copy do provides you what you need.
Demo[] array = new Demo[2];
array[0] = new Demo {X = 5.6, Y= 6.6};
array[1] = new Demo {X = 7.6, Y = 8.6};
GCHandle handle = GCHandle.Alloc(array, GCHandleType.Pinned);
try
{
IntPtr pointer = handle.AddrOfPinnedObject();
double[] copy = new double[array.Length*2];//This length may be calculated
Marshal.Copy(pointer, copy, 0, copy.Length);
}
finally
{
if (handle.IsAllocated)
handle.Free();
}
Since the struct is blittable, the array of the struct is blittable. Therefore you can pin the array of struct and copy into the double array with Marshal.Copy.
void CopyDemoArrayToDoubleArray(Demo[] demo, out double[] doubles)
{
doubles = new double[demo.Length * 2];
GCHandle gch = GCHandle.Alloc(demo, GCHandleType.Pinned);
try
{
IntPtr demoPtr = gch.AddrOfPinnedObject();
Marshal.Copy(demoPtr, doubles, 0, doubles.Length);
}
finally
{
gch.Free();
}
}
You might do well to benchmark this against the simpler for loop that you want to avoid. It is plausible that the for loop will perform perfectly adequately.
It's possible to write a generic method that can convert arrays of any compatible type (by "compatible" I mean "elements must be value types and the size of the elements must be compatible").
You can use P/Invoke to call the Windows API CopyMemory() method.
However, bear in mind that there may not be any performance advantage to doing it this way; you should perform careful timings to be sure.
[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);
public TOut[] ConvertArray<TIn, TOut>(TIn[] input) where TIn:struct where TOut:struct
{
if (input == null)
throw new ArgumentNullException("input");
int sizeTIn = Marshal.SizeOf(typeof(TIn));
int sizeTOut = Marshal.SizeOf(typeof(TOut));
int sizeBytes = input.Length*sizeTIn;
if ((sizeBytes % sizeTOut) != 0)
throw new ArgumentException("Size of input type is not compatible with size of output type.");
int sizeOut = sizeBytes/sizeTOut;
var output = new TOut[sizeOut];
GCHandle inHandle = GCHandle.Alloc(input, GCHandleType.Pinned);
GCHandle outHandle = GCHandle.Alloc(output, GCHandleType.Pinned);
try
{
IntPtr inPtr = inHandle.AddrOfPinnedObject();
IntPtr outPtr = outHandle.AddrOfPinnedObject();
CopyMemory(outPtr, inPtr, (uint)sizeBytes);
}
finally
{
outHandle.Free();
inHandle.Free();
}
return output;
}
For your example, you could call this like so:
Demo[] test = new Demo[10];
for (int i = 0; i < 10; ++i)
test[i] = new Demo {X = i, Y = i};
var result = ConvertArray<Demo, double>(test);
for (int i = 0; i < 20; ++i)
Console.WriteLine(result[i]);

C# - Cast a byte array to an array of struct and vice-versa (reverse)

I would like to save a Color[] to a file. To do so, I found that saving a byte array to a file using "System.IO.File.WriteAllBytes" should be very efficient.
I would like to cast my Color[] (array of struct) to a byte array into a safe way considering:
Potential problem of little endian / big endian (I think it is impossible to happen but want to be sure)
Having 2 differents pointer to the same memory which point to different type. Does the garbage collection will know what to do - moving objects - deleting a pointer ???
If it is possible, it would be nice to have a generic way to cast array of byte to array of any struct (T struct) and vice-versa.
If not possible, why ?
Thanks,
Eric
I think that those 2 solutions make a copy that I would like to avoid and also they both uses Marshal.PtrToStructure which is specific to structure and not to array of structure:
Reading a C/C++ data structure in C# from a byte array
How to convert a structure to a byte array in C#?
Since .NET Core 2.1, yes we can! Enter MemoryMarshal.
We will treat our Color[] as a ReadOnlySpan<Color>. We reinterpret that as a ReadOnlySpan<byte>. Finally, since WriteAllBytes has no span-based overload, we use a FileStream to write the span to disk.
var byteSpan = MemoryMarshal.AsBytes(colorArray.AsSpan());
fileStream.Write(byteSpan);
As an interesting side note, you can also experiment with the [StructLayout(LayoutKind.Explicit)] as an attribute on your fields. It allows you to specify overlapping fields, effectively allowing the concept of a union.
Here is a blog post on MSDN that illustrates this. It shows the following code:
[StructLayout(LayoutKind.Explicit)]
public struct MyUnion
{
[FieldOffset(0)]
public UInt16 myInt;
[FieldOffset(0)]
public Byte byte1;
[FieldOffset(1)]
public Byte byte2;
}
In this example, the UInt16 field overlaps with the two Byte fields.
This seems to be strongly related to what you are trying to do. It gets you very close, except for the part of writing all the bytes (especially of multiple Color objects) efficiently. :)
Regarding Array Type Conversion
C# as a language intentionally makes the process of flattening objects or arrays into byte arrays difficult because this approach goes against the principals of .NET strong typing. The conventional alternatives include several serialization tools which are generally seen a safer and more robust, or manual serialization coding such as BinaryWriter.
Having two variables of different types point to the same object in memory can only be performed if the types of the variables can be cast, implicitly or explicitly. Casting from an array of one element type to another is no trivial task: it would have to convert the internal members that keep track of things such as array length, etc.
A simple way to write and read Color[] to file
void WriteColorsToFile(string path, Color[] colors)
{
BinaryWriter writer = new BinaryWriter(File.OpenWrite(path));
writer.Write(colors.Length);
foreach(Color color in colors)
{
writer.Write(color.ToArgb());
}
writer.Close();
}
Color[] ReadColorsFromFile(string path)
{
BinaryReader reader = new BinaryReader(File.OpenRead(path));
int length = reader.ReadInt32();
Colors[] result = new Colors[length];
for(int n=0; n<length; n++)
{
result[n] = Color.FromArgb(reader.ReadInt32());
}
reader.Close();
}
You could use pointers if you really want to copy each byte and not have a copy but the same object, similar to this:
var structPtr = (byte*)&yourStruct;
var size = sizeof(YourType);
var memory = new byte[size];
fixed(byte* memoryPtr = memory)
{
for(int i = 0; i < size; i++)
{
*(memoryPtr + i) = *structPtr++;
}
}
File.WriteAllBytes(path, memory);
I just tested this and after adding the fixed block and some minor corrections it looks like it is working correctly.
This is what I used to test it:
public static void Main(string[] args)
{
var a = new s { i = 1, j = 2 };
var sPtr = (byte*)&a;
var size = sizeof(s);
var mem = new byte[size];
fixed (byte* memPtr = mem)
{
for (int i = 0; i < size; i++)
{
*(memPtr + i) = *sPtr++;
}
}
File.WriteAllBytes("A:\\file.txt", mem);
}
struct s
{
internal int i;
internal int j;
}
The result is the following:
(I manually resolved the hex bytes in the second line, only the first line was produced by the program)
public struct MyX
{
public int IntValue;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.U1)]
public byte[] Array;
MyX(int i, int b)
{
IntValue = b;
Array = new byte[3];
}
public MyX ToStruct(byte []ar)
{
byte[] data = ar;//= { 1, 0, 0, 0, 9, 8, 7 }; // IntValue = 1, Array = {9,8,7}
IntPtr ptPoit = Marshal.AllocHGlobal(data.Length);
Marshal.Copy(data, 0, ptPoit, data.Length);
MyX x = (MyX)Marshal.PtrToStructure(ptPoit, typeof(MyX));
Marshal.FreeHGlobal(ptPoit);
return x;
}
public byte[] ToBytes()
{
Byte[] bytes = new Byte[Marshal.SizeOf(typeof(MyX))];
GCHandle pinStructure = GCHandle.Alloc(this, GCHandleType.Pinned);
try
{
Marshal.Copy(pinStructure.AddrOfPinnedObject(), bytes, 0, bytes.Length);
return bytes;
}
finally
{
pinStructure.Free();
}
}
}
void function()
{
byte[] data = { 1, 0, 0, 0, 9, 8, 7 }; // IntValue = 1, Array = {9,8,7}
IntPtr ptPoit = Marshal.AllocHGlobal(data.Length);
Marshal.Copy(data, 0, ptPoit, data.Length);
var x = (MyX)Marshal.PtrToStructure(ptPoit, typeof(MyX));
Marshal.FreeHGlobal(ptPoit);
var MYstruc = x.ToStruct(data);
Console.WriteLine("x.IntValue = {0}", x.IntValue);
Console.WriteLine("x.Array = ({0}, {1}, {2})", x.Array[0], x.Array[1], x.Array[2]);
}
Working code for reference (take care, I did not need the alpha channel in my case):
// ************************************************************************
// If someday Microsoft make Color serializable ...
//public static void SaveColors(Color[] colors, string path)
//{
// BinaryFormatter bf = new BinaryFormatter();
// MemoryStream ms = new MemoryStream();
// bf.Serialize(ms, colors);
// byte[] bytes = ms.ToArray();
// File.WriteAllBytes(path, bytes);
//}
// If someday Microsoft make Color serializable ...
//public static Colors[] LoadColors(string path)
//{
// Byte[] bytes = File.ReadAllBytes(path);
// BinaryFormatter bf = new BinaryFormatter();
// MemoryStream ms2 = new MemoryStream(bytes);
// return (Colors[])bf.Deserialize(ms2);
//}
// ******************************************************************
public static void SaveColorsToFile(Color[] colors, string path)
{
var formatter = new BinaryFormatter();
int count = colors.Length;
using (var stream = File.OpenWrite(path))
{
formatter.Serialize(stream, count);
for (int index = 0; index < count; index++)
{
formatter.Serialize(stream, colors[index].R);
formatter.Serialize(stream, colors[index].G);
formatter.Serialize(stream, colors[index].B);
}
}
}
// ******************************************************************
public static Color[] LoadColorsFromFile(string path)
{
var formatter = new BinaryFormatter();
Color[] colors;
using (var stream = File.OpenRead(path))
{
int count = (int)formatter.Deserialize(stream);
colors = new Color[count];
for (int index = 0; index < count; index++)
{
byte r = (byte)formatter.Deserialize(stream);
byte g = (byte)formatter.Deserialize(stream);
byte b = (byte)formatter.Deserialize(stream);
colors[index] = Color.FromRgb(r, g, b);
}
}
return colors;
}
// ******************************************************************

how can read The Bytes of an object variable (that its in RAM now) and recreate the object by it Bytes in C#?

i want to access the bytes of an object in C# okey for example :
Serializing an class in WCF the serializer read the all bytes of class object and Finally SOAP message !
Some Thing Like This You Know A Way To Read object Bytes And Recreate object by its Bytes
//x_obj created and we allocate an address & size of RAM (memory) to it Now its byts structure is in the RAM
X_calss x_obj = new X_class();
//and now we want to read the x_obj bytes in RAM
unsafe byte[] READ(X_class x_obj){
xobj_pointer = &x_obj;//pointer to address of obj in RAM
byte[] xobj_bytes = read_from(xobj_pointer,sizeof(x_obj));//Some Way To Read Byte of x_obj
return xobj_bytes;
}
// and now we want to recreate class by it bytes stream
unsafe X_class Creative(byte[] xobj_bytes){
x_pointer = Memory_allocate(sizeof(X_class));//reserve an address of RAM
write_to(x_pointer,xobj_bytes);//write bytes to RAM
X_class x_obj = (X_class)*(x_pointer);//recreate the class by content of pointer
return x_obj;
}
i want to access the bytes of an object in C# okey for example
You absolutely don't. Two reasons just to start with:
The object layout may well be different on the server and the client, particularly if one is using a 64-bit CLR and the other is using a 32-bit CLR
Objects contain process-specific data such as syncblocks, which you can't sensibly propagate
Any references on the "source" machine probably won't be valid on the "target" machine
Consider the bytes involved in this simple class:
class Foo
{
string name;
}
The "bytes" of the data here just consist of a reference. For the purposes of this question, let's imagine that a reference is actually a simple pointer. A pointer to memory address 0x12345678 is very unlikely to refer to a string with the same contents when you've copied that address over to another process.
This kind of thing is precisely why we have serializers: taking a copy of the memory itself simply isn't good enough.
C# is not equivalent to C in terms of operating with memory.
1.Mark the class of the object with Serializable attribute.
2.Serialize to bytes:
public byte[] ObjectToByteArray(Object obj)
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, obj);
return ms.ToArray();
}
3.Deserialize to object:
public object BytesToObject(byte[] bytes)
{
MemoryStream ms = new MemoryStream(bytes);
BinaryFormatter bf= new BinaryFormatter();
ms.Position = 0;
return bf.Deserialize(ms);
}
While agreeing with both Jon Skeet and ValtasarIII, getting access to the raw bytes of a variable is possible (in a horrible kind of way) if your data types are structs (with known layouts), contain only value types and you're allowing unsafe code.
What follows is certainly not a technique that you'd want to use to cross machine boundaries (nor should it really be used at all).
Given a structure called TestStruct defined like so
[StructLayout(LayoutKind.Sequential)]
public struct TestStruct
{
public int A;
public int B;
}
The raw bytes of its contents may be obtained like this
private static unsafe byte[] GetBytes(TestStruct item)
{
//Figure out how big TestStruct is
var size = Marshal.SizeOf(item);
//Make an array large enough to hold all the bytes required
var array = new byte[size];
//Get a pointer to the struct
var itemPtr = &item;
//Change the type of the pointer from TestStruct* to byte*
var itemBytes = (byte*) itemPtr;
//Iterate from the first byte in the data to the last, copying the values into our
// temporary storage
for (var i = 0; i < size; ++i)
{
array[i] = itemBytes[i];
}
//Return the bytes that were found in the instance of TestStruct
return array;
}
And we can build a new one like this
private static unsafe TestStruct Reconstitute(IList<byte> data)
{
//Figure out how big TestStruct is
var size = Marshal.SizeOf(typeof(TestStruct));
//If the data we've been presented with is either too large or too small to contain
// the data for exactly one TestStruct instance, throw an exception
if (data.Count != size)
{
throw new InvalidOperationException("Amount of data available is not the exact amount of data required to reconstitute the item");
}
//Make our temporary instance
var item = new TestStruct();
//Get a pointer to our temporary instance
var itemPtr = &item;
//Change the type of the pointer to byte*
var itemBytes = (byte*) itemPtr;
//Iterate from the first byte in the data to the last, copying the values into our
// temporary instance
for (var i = 0; i < size; ++i)
{
itemBytes[i] = data[i];
}
//Return our reconstituted structure
return item;
}
Usage:
static void Main()
{
var test = new TestStruct
{
A = 1,
B = 3
};
var bytes = GetBytes(test);
var duplicate = Reconstitute(bytes);
Console.WriteLine("Original");
PrintObject(test, 1);
Console.WriteLine();
Console.WriteLine("Reconstituted");
PrintObject(duplicate, 1);
Console.ReadLine();
}
And, for completeness, the code for PrintObject
static void PrintObject(object instance, int initialIndentLevel)
{
PrintObject(instance, initialIndentLevel, 4, ' ', new List<object>());
}
static void PrintObject(object instance, int level, int indentCount, char paddingChar, ICollection<object> printedObjects)
{
if (printedObjects.Contains(instance))
{
return;
}
var tabs = "".PadLeft(level * indentCount, paddingChar);
var instanceType = instance.GetType();
printedObjects.Add(instance);
foreach (var member in instanceType.GetMembers())
{
object value;
try
{
switch (member.MemberType)
{
case MemberTypes.Property:
var property = (PropertyInfo) member;
value = property.GetValue(instance, null);
break;
case MemberTypes.Field:
var field = (FieldInfo) member;
value = field.GetValue(instance);
break;
default:
continue;
}
}
catch
{
continue;
}
if (value == null || value.GetType().IsValueType || value.GetType().ToString() != value.ToString())
{
Console.WriteLine("{2}{0}: {1}", member.Name, (value ?? "(null)"), tabs);
}
else
{
var vals = value as IEnumerable;
if (vals != null)
{
var index = 0;
var indented = "".PadLeft((level + 1) * indentCount, paddingChar);
Console.WriteLine("{2}{0}: {1}", member.Name, value, tabs);
foreach (var val in vals)
{
Console.WriteLine("{1}[{0}]:", index++, indented);
PrintObject(val, level + 2, indentCount, paddingChar, printedObjects);
}
if (index == 0)
{
Console.WriteLine("{0}(No elements)", indented);
}
}
else
{
PrintObject(value, level + 1, indentCount, paddingChar, printedObjects);
}
}
}
}

Marshal.PtrToStructure (and back again) and generic solution for endianness swapping

I have a system where a remote agent sends serialized structures (from an embedded C system) for me to read and store via IP/UDP. In some cases I need to send back the same structure types. I thought I had a nice setup using Marshal.PtrToStructure (receive) and Marshal.StructureToPtr (send). However, a small gotcha is that the network big endian integers need to be converted to my x86 little endian format to be used locally. When I'm sending them off again, big endian is the way to go.
Here are the functions in question:
private static T BytesToStruct<T>(ref byte[] rawData) where T: struct
{
T result = default(T);
GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
try
{
IntPtr rawDataPtr = handle.AddrOfPinnedObject();
result = (T)Marshal.PtrToStructure(rawDataPtr, typeof(T));
}
finally
{
handle.Free();
}
return result;
}
private static byte[] StructToBytes<T>(T data) where T: struct
{
byte[] rawData = new byte[Marshal.SizeOf(data)];
GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
try
{
IntPtr rawDataPtr = handle.AddrOfPinnedObject();
Marshal.StructureToPtr(data, rawDataPtr, false);
}
finally
{
handle.Free();
}
return rawData;
}
And a quick example structure that might be used like this:
byte[] data = this.sock.Receive(ref this.ipep);
Request request = BytesToStruct<Request>(ref data);
Where the structure in question looks like:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
private struct Request
{
public byte type;
public short sequence;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public byte[] address;
}
What (generic) way can I swap the endianness when marshalling the structures? My need is such that the locally stored 'request.sequence' in this example should be little-endian for displaying to the user. I don't want to have to swap the endianness in a structure-specific way since it's a generic problem.
My first thought was to use Reflection, but I'm not very familiar with that feature. Also, I hoped that there would be a better solution out there that somebody could point me towards. Thanks in advance :)
Reflection does seem like the only real way to accomplish what you're after.
I've put together some code below. It creates an attribute called EndianAttribute that can be applied at the field level on a struct. I've included the definition for this attribute and it's associated enum, as well as the modifications to your code necessary to use it.
As a side note, you did not need to define rawData as a ref parameter.
Note that this does require the use of C# 3.0/.NET 3.5, since I'm using LINQ and anonymous types in the function doing the work. It would not be difficult to rewrite the function without these features, though.
[AttributeUsage(AttributeTargets.Field)]
public class EndianAttribute : Attribute
{
public Endianness Endianness { get; private set; }
public EndianAttribute(Endianness endianness)
{
this.Endianness = endianness;
}
}
public enum Endianness
{
BigEndian,
LittleEndian
}
private static void RespectEndianness(Type type, byte[] data)
{
var fields = type.GetFields().Where(f => f.IsDefined(typeof(EndianAttribute), false))
.Select(f => new
{
Field = f,
Attribute = (EndianAttribute)f.GetCustomAttributes(typeof(EndianAttribute), false)[0],
Offset = Marshal.OffsetOf(type, f.Name).ToInt32()
}).ToList();
foreach (var field in fields)
{
if ((field.Attribute.Endianness == Endianness.BigEndian && BitConverter.IsLittleEndian) ||
(field.Attribute.Endianness == Endianness.LittleEndian && !BitConverter.IsLittleEndian))
{
Array.Reverse(data, field.Offset, Marshal.SizeOf(field.Field.FieldType));
}
}
}
private static T BytesToStruct<T>(byte[] rawData) where T : struct
{
T result = default(T);
RespectEndianness(typeof(T), rawData);
GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
try
{
IntPtr rawDataPtr = handle.AddrOfPinnedObject();
result = (T)Marshal.PtrToStructure(rawDataPtr, typeof(T));
}
finally
{
handle.Free();
}
return result;
}
private static byte[] StructToBytes<T>(T data) where T : struct
{
byte[] rawData = new byte[Marshal.SizeOf(data)];
GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
try
{
IntPtr rawDataPtr = handle.AddrOfPinnedObject();
Marshal.StructureToPtr(data, rawDataPtr, false);
}
finally
{
handle.Free();
}
RespectEndianness(typeof(T), rawData);
return rawData;
}
For those of us without Linq, a replacement RespectEndianness():
private static void RespectEndianness(Type type, byte[] data) {
foreach (FieldInfo f in type.GetFields()) {
if (f.IsDefined(typeof(EndianAttribute), false)) {
EndianAttribute att = (EndianAttribute)f.GetCustomAttributes(typeof(EndianAttribute), false)[0];
int offset = Marshal.OffsetOf(type, f.Name).ToInt32();
if ((att.Endianness == Endianness.BigEndian && BitConverter.IsLittleEndian) ||
(att.Endianness == Endianness.LittleEndian && !BitConverter.IsLittleEndian)) {
Array.Reverse(data, offset, Marshal.SizeOf(f.FieldType));
}
}
}
}
Here's my variation - it handles nested structs and arrays, with the assumption that arrays are of a fixed size, eg marked with a [MarshalAs(UnmanagedType.ByValArray, SizeConst = N)] attribute.
public static class Serializer
{
public static byte[] GetBytes<T>(T structure, bool respectEndianness = true) where T : struct
{
var size = Marshal.SizeOf(structure); //or Marshal.SizeOf<T>(); in .net 4.5.1
var bytes = new byte[size];
var ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(structure, ptr, true);
Marshal.Copy(ptr, bytes, 0, size);
Marshal.FreeHGlobal(ptr);
if (respectEndianness) RespectEndianness(typeof(T), bytes);
return bytes;
}
public static T FromBytes<T>(byte[] bytes, bool respectEndianness = true) where T : struct
{
var structure = new T();
if (respectEndianness) RespectEndianness(typeof(T), bytes);
int size = Marshal.SizeOf(structure); //or Marshal.SizeOf<T>(); in .net 4.5.1
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(bytes, 0, ptr, size);
structure = (T)Marshal.PtrToStructure(ptr, structure.GetType());
Marshal.FreeHGlobal(ptr);
return structure;
}
private static void RespectEndianness(Type type, byte[] data, int offSet = 0)
{
var fields = type.GetFields()
.Select(f => new
{
Field = f,
Offset = Marshal.OffsetOf(type, f.Name).ToInt32(),
}).ToList();
foreach (var field in fields)
{
if (field.Field.FieldType.IsArray)
{
//handle arrays, assuming fixed length
var attr = field.Field.GetCustomAttributes(typeof(MarshalAsAttribute), false).FirstOrDefault();
var marshalAsAttribute = attr as MarshalAsAttribute;
if (marshalAsAttribute == null || marshalAsAttribute.SizeConst == 0)
throw new NotSupportedException(
"Array fields must be decorated with a MarshalAsAttribute with SizeConst specified.");
var arrayLength = marshalAsAttribute.SizeConst;
var elementType = field.Field.FieldType.GetElementType();
var elementSize = Marshal.SizeOf(elementType);
var arrayOffset = field.Offset + offSet;
for (int i = arrayOffset; i < arrayOffset + elementSize * arrayLength; i += elementSize) {
RespectEndianness(elementType, data, i);
}
}
else if (!field.Field.FieldType.IsPrimitive) //or !field.Field.FiledType.GetFields().Length == 0
{
//handle nested structs
RespectEndianness(field.Field.FieldType, data, field.Offset);
}
else
{
//handle primitive types
Array.Reverse(data, offSet + field.Offset, Marshal.SizeOf(field.Field.FieldType));
}
}
}
}
This question was awesome and helped me a lot! I needed to expand on the endian changer though as it doesn't seem to handle arrays or structs within structs.
public struct mytest
{
public int myint;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public int[] ptime;
}
public static void SwapIt(Type type, byte[] recvbyte, int offset)
{
foreach (System.Reflection.FieldInfo fi in type.GetFields())
{
int index = Marshal.OffsetOf(type, fi.Name).ToInt32() + offset;
if (fi.FieldType == typeof(int))
{
Array.Reverse(recvbyte, index, sizeof(int));
}
else if (fi.FieldType == typeof(float))
{
Array.Reverse(recvbyte, index, sizeof(float));
}
else if (fi.FieldType == typeof(double))
{
Array.Reverse(recvbyte, index, sizeof(double));
}
else
{
// Maybe we have an array
if (fi.FieldType.IsArray)
{
// Check for MarshalAs attribute to get array size
object[] ca = fi.GetCustomAttributes(false);
if (ca.Count() > 0 && ca[0] is MarshalAsAttribute)
{
int size = ((MarshalAsAttribute)ca[0]).SizeConst;
// Need to use GetElementType to see that int[] is made of ints
if (fi.FieldType.GetElementType() == typeof(int))
{
for (int i = 0; i < size; i++)
{
Array.Reverse(recvbyte, index + (i * sizeof(int)), sizeof(int));
}
}
else if (fi.FieldType.GetElementType() == typeof(float))
{
for (int i = 0; i < size; i++)
{
Array.Reverse(recvbyte, index + (i * sizeof(float)), sizeof(float));
}
}
else if (fi.FieldType.GetElementType() == typeof(double))
{
for (int i = 0; i < size; i++)
{
Array.Reverse(recvbyte, index + (i * sizeof(double)), sizeof(double));
}
}
else
{
// An array of something else?
Type t = fi.FieldType.GetElementType();
int s = Marshal.SizeOf(t);
for (int i = 0; i < size; i++)
{
SwapIt(t, recvbyte, index + (i * s));
}
}
}
}
else
{
SwapIt(fi.FieldType, recvbyte, index);
}
}
}
}
Note this code was only tested on structs made of int, float, double. Will probably mess up if you have a string in there!

Categories

Resources