Write contiguous structured data to a binary file using C# - c#

I am doing a project in which I need to write structured data into a binary file. First I need to write a header, then fetch data from somewhere, populate and write the structured data blocks to the said file. I am porting C structs to C# as follows:
C header struct:
typedef struct
{
DWORD uSignature;
DWORD uRecordLength;
} Header;
C data struct:
typedef struct
{
DWORD uCode; // a two character identifier
char uLabel[10];
int uDate;
float uData[37];
} MyData;
Here is the C# header struct:
struct Header
{
public uint uSignature;
public uint uRecordLength;
}
and here is the C# data struct:
struct MyData
{
public MyData (int Count) : this ()
{
uData = new Single[Count];
}
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] uCode;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public byte[] uLabel;
public int uDate;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 37)]
public Single [] uData;
}
This file will be read by another application which can read from the binary file if data correctly formatted. I printed out the size of the two struct types and they look good. However, the output file can not be read by the said application.
So I have two questions:
Are the data types and Marshals I used correctly in C to C# conversion?
I use FileStream and BinaryWriter to write to the binary file. All data (header and subsequent data) must be in sequence (contiguous) . As I create and write data structs on the fly, I am not sure how to allocate continuous memory using something like:
public static byte[] GetData(object obj)
{
var size = Marshal.SizeOf(obj.GetType());
var data = new byte[size];
IntPtr pnt = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(obj, pnt, true);
// Copy the array to unmanaged memory.
Marshal.Copy(pnt, data, 0, size);
return data;
}
finally
{
// Free the unmanaged memory.
Marshal.FreeHGlobal(pnt);
}
}
Any help would be greatly appreciated!
[EDIT]
I added two methods to convert specific struct data into byte array, but the file is still unreadable:
private byte[] DataToByteArray(MyData data)
{
int len = 0;
var size = Marshal.SizeOf(data.GetType());
var barray = new byte[size];
data.uCode.CopyTo(barray, 0);
len += data.uCode.Length;
data.uLabel.CopyTo(barray, len);
len += data.uLabel.Length;
BitConverter.GetBytes(0).CopyTo(barray, len);
len += data.uData.Length;
Buffer.BlockCopy(data.uData, 0, barray, len, data.uData.Length);
return barray;
}
private byte[] HeadToByteArray(Header data)
{
var size = Marshal.SizeOf(data.GetType());
var barray = new byte[size];
BitConverter.GetBytes(data.uSignature).CopyTo(barray, 0);
BitConverter.GetBytes(data.uRecordLength).CopyTo(barray, 4);
return barray;
}
【EDIT2】
Here is how it works in C:
#define NQ_EX 'QN'
FILE *fout;
fopen_s(&fout, "path_to_the_file", "wb");
Header head = { val1, sizeof(MyData) };
fwrite(&head, sizeof(Header), 1, fout);
while (!stop && data_is_coming)
{
MyData data;
memset(&data, 0, sizeof(data));
data.uCode = NQ_EX;
sprintf_s(data.uLabel, "%s", getVal("field1"));
data.uData[0] = getVal("field2");
data.uData[1] = getVal("field3");
....
fwrite(&data, sizeof(MyData), 1, fout);
}

The endianness seems to be fine. After some changes and testing with the help of Jeroen and others, I am able to make it work. The problem was due to the Block.copy method. I change it to Array.copy as follows:
private byte[] DataToByteArray(MyData data)
{
int len = 0;
var size = Marshal.SizeOf(data.GetType());
var barray = new byte[size];
data.uCode.CopyTo(barray, 0);
len += data.uCode.Length;
data.uLabel.CopyTo(barray, len);
len += data.uLabel.Length;
BitConverter.GetBytes(0).CopyTo(barray, len);
len += data.uData.Length;
for (int i = 0; i < data.uData.Length; i++)
Array.Copy(BitConverter.GetBytes(data.uData[i]), 0, barray, len+i * 4, 4);
return barray;
}

Related

Marshaling C# struct with array of structs and size param index

I've read several topics about but I still can't understand the real limitation of not being able to convert this structure to byte array easily:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct B {
public int b_a;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct A {
public int sizeB;
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
public B[] b;
}
I'm writing a TCP communication program so I want to build my S2C packets in a struct and then send them as byte[] so I'm looking for the cheapest and fastest way to achieve this.
I have already tried Marsheling in many ways but there is always some exception in Marshal.SizeOf().
In this example I get the following error: "[...] cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed."
Struct initialization eg.:
A a = new A();
B[] b = new B[5];
a.sizeB = 5;
a.b = b;
Marshal.SizeOf(a);
You don't have the same control for low level memory access like you would C or C++. You will need to do some manual work when you have an array of undefined length in C#.
Here are a couple ways of accomplishing that.
struct B
{
public int b_a;
}
struct A
{
public int sizeB;
public B[] b;
}
The first being a BinaryWriter. This can be faster if your structure does not have a lot of fields.
static byte[] ConvertToByte(A a)
{
using (var ms = new MemoryStream())
using (var writer = new BinaryWriter(ms))
{
writer.Write(a.sizeB);
foreach (var b in a.b)
writer.Write(b.b_a);
return ms.ToArray();
}
}
The other to use marshalling like you were but explicitly looping through the array.
static byte[] ConvertToByte(A a)
{
var bStructSize = Marshal.SizeOf<B>();
var size = bStructSize * a.b.Length;
var arr = new byte[size + 4];
var ptr = Marshal.AllocHGlobal(size);
for (int i = 0; i < a.b.Length; i++)
Marshal.StructureToPtr(a.b[i], ptr + i * bStructSize, true);
Marshal.Copy(ptr, arr, 4, size);
Array.Copy(BitConverter.GetBytes(a.sizeB), arr, 4);
return arr;
}

how to transform byte[] to struct (contains a byte[] member and length member)

i have a struct define as:
[StructLayout(LayoutKind.Sequential,CharSet = CharSet.Ansi,Pack = 1)]
internal struct Message
{
[MarshalAs(UnmanagedType.U1, SizeConst = 1)]
public byte age;
[MarshalAs(UnmanagedType.U2, SizeConst = 2)]
public ushort length;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1,SizeConst = 502)]
public byte[] data;
}
payload received via udp,when received byte[],need to be converted to struct.
data length specified as 502,but actually it should be the length member value indicating the data length,if remove the SizeConst attr, the code will throw Marshal exception at Marshal.SizeOf(typeof(T)).
public static T ToStruct<T>(this byte[] buf)
{
var lth = Marshal.SizeOf(typeof(T));
if (lth > buf.Length) return default(T);
var ptr = Marshal.AllocHGlobal(lth);
Marshal.Copy(buf, 0, ptr, lth);
var structure = Marshal.PtrToStructure(ptr, typeof(T));
Marshal.FreeHGlobal(ptr);
return (T)structure;
}
exception info:
System.ArgumentException: Type 'Itms.Agent.IotBox.TieTa.Entity.Message' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.
i know this is very simple to handle in c/c++, but C# doesn't have a similar approach.
any help?
when received byte[],need to be converted to struct. data length specified as 502,but actually it should be the length member value indicating the data length
that's too complex for simple marshal operations. You'll probably have to serialize/deserialize manually, i.e. something like:
byte[] payload = ...
var age = payload[0];
var len = (payload[1] << 8) | payload[2]; // invert depending on endianness
byte[] data = len == 0 ? Array.Empty<byte>() : new byte[len];
Buffer.BlockCopy(payload, 3, data, len);
var msg = new Message(age, len, data);
This would mean you could remove all the attributes, as you're not using any marshalling features.
Also... the length field seems kinda redundant, since it is simply duplicating the array's length.

How to do memcpy in C# .Net CF with the following task

Hi I am trying to convert the C/C++ Strcut to C# and how to fill the structure member with address of another structure in C#?
C/C++ Struct looks like:
typedef struct _NDISUIO_QUERY_OID
{
NDIS_OID Oid;
PTCHAR ptcDeviceName;
UCHAR Data[sizeof(ULONG)];
} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
typedef struct My_Struct
{
//les have 2 variables...
UINT a;
UINT b;
}My_STATS, *PMy_STATS;
PNDISUIO_QUERY_OID pQueryOid = NULL;
pQueryOid = (PNDISUIO_QUERY_OID)malloc(sizeof(NDISUIO_QUERY_OID)+ sizeof(My_STATS)) ;
PMy_STATS Statistics;
pQueryOid->Oid = ulOIDCode;//Required OID
pQueryOid->ptcDeviceName = AUB_NAME;//REquired STRING
memcpy(pQueryOid->Data, Statistics, sizeof(My_STATS));
My C# Struct is:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _NDISUIO_QUERY_OID
{
public uint Oid;
[MarshalAs(UnmanagedType.LPWStr)]
public string ptcDeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = sizeof(uint))]
public string Data;
};
Problem: How to copy the Statistics structure to Data array in C#??
Thanks :)
Here's my implementation (FYI, the SDF contains all of this code and a lot more)
internal class NDISQueryOid
{
protected const int NDISUIO_QUERY_OID_SIZE = 12;
protected byte[] m_data;
public int Size { get; private set; }
public NDISQueryOid(byte[] data)
{
int extrasize = data.Length;
Size = 8 + extrasize;
m_data = new byte[Size];
Buffer.BlockCopy(data, 0, m_data, DataOffset, data.Length);
}
public NDISQueryOid(int extrasize)
{
Size = NDISUIO_QUERY_OID_SIZE + extrasize;
m_data = new byte[Size];
}
protected const int OidOffset = 0;
public uint Oid
{
get { return BitConverter.ToUInt32(m_data, OidOffset); }
set
{
byte[] bytes = BitConverter.GetBytes(value);
Buffer.BlockCopy(bytes, 0, m_data, OidOffset, 4);
}
}
protected const int ptcDeviceNameOffset = OidOffset + 4;
public unsafe byte* ptcDeviceName
{
get
{
return (byte*)BitConverter.ToUInt32(m_data, ptcDeviceNameOffset);
}
set
{
byte[] bytes = BitConverter.GetBytes((UInt32)value);
Buffer.BlockCopy(bytes, 0, m_data, ptcDeviceNameOffset, 4);
}
}
protected const int DataOffset = ptcDeviceNameOffset + 4;
public byte[] Data
{
get
{
byte[] b = new byte[Size - DataOffset];
Array.Copy(m_data, DataOffset, b, 0, Size - DataOffset);
return b;
}
set
{
Size = 8 + value.Length;
m_data = new byte[Size];
Buffer.BlockCopy(value, 0, m_data, DataOffset, value.Length);
}
}
public byte[] getBytes()
{
return m_data;
}
public static implicit operator byte[](NDISQueryOid qoid)
{
return qoid.m_data;
}
}
Note that in my usage, the NDIS IOCT takes in a pointer (most of my NDIS work is all done as unsafe) so you'd have to do some adjustment there.
So if, for example, you're querying the BSSID, I know the BSSID data is 36 bytes, so I'd create something like this:
var queryOID = new NDISQueryOid(36);
then allocate the name and call NDIS (the production code has a lot more checking than this):
byte[] nameBytes = System.Text.Encoding.Unicode.GetBytes(adapterName + '\0');
fixed (byte* pName = &nameBytes[0])
{
queryOID.ptcDeviceName = pName;
queryOID.Oid = (uint)oid;
var bytes = queryOID.getBytes();
ndis.DeviceIoControl(IOCTL_NDISUIO_QUERY_OID_VALUE, bytes, bytes);
var result = new byte[queryOID.Data.Length];
Buffer.BlockCopy(queryOID.Data, 0, result, 0, result.Length);
}
EDIT
So the result member above is a byte array of the "result" of the query. What it means and how you interpret it depends on what the OID you queried was. For example, if you were querying the currently connected SSID (i.e. NDIS_OID.SSID), then that comes back as a 4-byte length followed by the ASCII-encoded name, so you'd decipher it like this:
int len = BitConverter.ToInt32(data, 0);
if (len > 0)
{
ssid = System.Text.Encoding.ASCII.GetString(data, 4, len);
}
But again, this is only for one specific OID. You have to handle every return case for every incoming OID you decide to support.
First you have the wrong translation of your C++ code: the C# equivalent of a C++ char[] is not a string, it's a byte[]. Once you have that, you just need to know, in general, how to copy a structure into a byte array. Here's a compilable example:
using System;
using System.Runtime.InteropServices;
struct Dest
{
public byte[] Data;
}
struct Src
{
public GCHandle StringHandle;
public long A;
public long B;
}
class Program
{
static void Main()
{
Copy();
}
static void Copy()
{
var str = "Hello";
var src = new Src {
A = 3,
B = 4,
StringHandle = GCHandle.Alloc(str, GCHandleType.Normal)
};
var dst = new Dest();
unsafe
{
Src* srcPtr = &src;
dst.Data = new byte[sizeof(Src)];
Marshal.Copy((IntPtr)srcPtr, dst.Data, 0, sizeof(Src));
}
// When you're sure no one can reference the string anymore
// (Including by accessing the data you put in dst.Data!)
src.StringHandle.Free();
}
EDIT: added example of how to deal with reference types such as strings.
Safely, you can't. .NET enforces type safety, which means that you simply can't force a string to be a structure. However, you can look at the data instead of doing unsafe type casts (why are you storing two uints in a string in the first place? And marshalling it as unicode?
First, you'll have to make Data a byte array. It might be possible to do this with a string as well, but that's just adding encoding issues to the mix; if you can, use byte[] instead. Also, if you don't need to have different kinds of data inside (it seems so), you could simply put the two uint fields right inside the struct and it should work just fine:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _NDISUIO_QUERY_OID
{
public uint Oid;
[MarshalAs(UnmanagedType.LPWStr)]
public string ptcDeviceName;
public uint DataA;
public uint DataB;
};
The second approach would use a const-sized byte array, long enough to hold the two uints:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _NDISUIO_QUERY_OID
{
public uint Oid;
[MarshalAs(UnmanagedType.LPWStr)]
public string ptcDeviceName;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = sizeof(ulong))]
public byte[] Data;
};
The first four bytes will be the first uint, the next will be the second.
And of course, you could also use a .NET struct the same way as in the original code - just make sure you use the correct datatype in _NDISUIO_QUERY_OID and it should work automagically.
One point to note though, it seems that the data returned isn't actually necessarily fixed-length. That is quite tricky and it basically means you'd have to deserialize the structure manually based on the pointer and length you get.

Struct to byte array? [duplicate]

How do I convert a structure to a byte array in C#?
I have defined a structure like this:
public struct CIFSPacket
{
public uint protocolIdentifier; //The value must be "0xFF+'SMB'".
public byte command;
public byte errorClass;
public byte reserved;
public ushort error;
public byte flags;
//Here there are 14 bytes of data which is used differently among different dialects.
//I do want the flags2. However, so I'll try parsing them.
public ushort flags2;
public ushort treeId;
public ushort processId;
public ushort userId;
public ushort multiplexId;
//Trans request
public byte wordCount;//Count of parameter words defining the data portion of the packet.
//From here it might be undefined...
public int parametersStartIndex;
public ushort byteCount; //Buffer length
public int bufferStartIndex;
public string Buffer;
}
In my main method, I create an instance of it and assign values to it:
CIFSPacket packet = new CIFSPacket();
packet.protocolIdentifier = 0xff;
packet.command = (byte)CommandTypes.SMB_COM_NEGOTIATE;
packet.errorClass = 0xff;
packet.error = 0;
packet.flags = 0x00;
packet.flags2 = 0x0001;
packet.multiplexId = 22;
packet.wordCount = 0;
packet.byteCount = 119;
packet.Buffer = "NT LM 0.12";
Now I want to send this Packet by socket. For that, I need to convert the structure to a byte array. How can I do it?
My full code is as follows.
static void Main(string[] args)
{
Socket MyPing = new Socket(AddressFamily.InterNetwork,
SocketType.Stream , ProtocolType.Unspecified ) ;
MyPing.Connect("172.24.18.240", 139);
//Fake an IP Address so I can send with SendTo
IPAddress IP = new IPAddress(new byte[] { 172,24,18,240 });
IPEndPoint IPEP = new IPEndPoint(IP, 139);
//Local IP for Receiving
IPEndPoint Local = new IPEndPoint(IPAddress.Any, 0);
EndPoint EP = (EndPoint)Local;
CIFSPacket packet = new CIFSPacket();
packet.protocolIdentifier = 0xff;
packet.command = (byte)CommandTypes.SMB_COM_NEGOTIATE;
packet.errorClass = 0xff;
packet.error = 0;
packet.flags = 0x00;
packet.flags2 = 0x0001;
packet.multiplexId = 22;
packet.wordCount = 0;
packet.byteCount = 119;
packet.Buffer = "NT LM 0.12";
MyPing.SendTo(It takes byte array as parameter);
}
What would a code snippet be?
This is fairly easy, using marshalling.
Top of file
using System.Runtime.InteropServices
Function
byte[] getBytes(CIFSPacket str) {
int size = Marshal.SizeOf(str);
byte[] arr = new byte[size];
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(str, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
return arr;
}
And to convert it back:
CIFSPacket fromBytes(byte[] arr)
{
CIFSPacket str = new CIFSPacket();
int size = Marshal.SizeOf(str);
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(arr, 0, ptr, size);
str = (CIFSPacket)Marshal.PtrToStructure(ptr, str.GetType());
}
finally
{
Marshal.FreeHGlobal(ptr);
}
return str;
}
In your structure, you will need to put this before a string
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string Buffer;
And make sure SizeConst is as big as your biggest possible string.
And you should probably read this:
http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx
If you really want it to be FAST on Windows, you can do it using unsafe code with CopyMemory. CopyMemory is about 5x faster (e.g. 800MB of data takes 3s to copy via marshalling, while only taking .6s to copy via CopyMemory). This method does limit you to using only data which is actually stored in the struct blob itself, e.g. numbers, or fixed length byte arrays.
[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
private static unsafe extern void CopyMemory(void *dest, void *src, int count);
private static unsafe byte[] Serialize(TestStruct[] index)
{
var buffer = new byte[Marshal.SizeOf(typeof(TestStruct)) * index.Length];
fixed (void* d = &buffer[0])
{
fixed (void* s = &index[0])
{
CopyMemory(d, s, buffer.Length);
}
}
return buffer;
}
Have a look at these methods:
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;
}
void ByteArrayToStructure(byte [] bytearray, ref object obj)
{
int len = Marshal.SizeOf(obj);
IntPtr i = Marshal.AllocHGlobal(len);
Marshal.Copy(bytearray,0, i,len);
obj = Marshal.PtrToStructure(i, obj.GetType());
Marshal.FreeHGlobal(i);
}
This is a shameless copy of another thread which I found upon Googling!
Update : For more details, check the source
Variant of the code of Vicent with one less memory allocation:
public static byte[] GetBytes<T>(T str)
{
int size = Marshal.SizeOf(str);
byte[] arr = new byte[size];
GCHandle h = default(GCHandle);
try
{
h = GCHandle.Alloc(arr, GCHandleType.Pinned);
Marshal.StructureToPtr<T>(str, h.AddrOfPinnedObject(), false);
}
finally
{
if (h.IsAllocated)
{
h.Free();
}
}
return arr;
}
public static T FromBytes<T>(byte[] arr) where T : struct
{
T str = default(T);
GCHandle h = default(GCHandle);
try
{
h = GCHandle.Alloc(arr, GCHandleType.Pinned);
str = Marshal.PtrToStructure<T>(h.AddrOfPinnedObject());
}
finally
{
if (h.IsAllocated)
{
h.Free();
}
}
return str;
}
I use GCHandle to "pin" the memory and then I use directly its address with h.AddrOfPinnedObject().
I know this is really late, but with C# 7.3 you can do this for unmanaged structs or anything else that's unmanged (int, bool etc...):
public static unsafe byte[] ConvertToBytes<T>(T value) where T : unmanaged {
byte* pointer = (byte*)&value;
byte[] bytes = new byte[sizeof(T)];
for (int i = 0; i < sizeof(T); i++) {
bytes[i] = pointer[i];
}
return bytes;
}
Then use like this:
struct MyStruct {
public int Value1;
public int Value2;
//.. blah blah blah
}
byte[] bytes = ConvertToBytes(new MyStruct());
As the main answer is using CIFSPacket type, which is not (or no longer) available in C#, I wrote correct methods:
static byte[] getBytes(object str)
{
int size = Marshal.SizeOf(str);
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(str, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
static T fromBytes<T>(byte[] arr)
{
T str = default(T);
int size = Marshal.SizeOf(str);
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(arr, 0, ptr, size);
str = (T)Marshal.PtrToStructure(ptr, str.GetType());
Marshal.FreeHGlobal(ptr);
return str;
}
Tested, they work.
You can use Marshal (StructureToPtr, ptrToStructure), and Marshal.copy but this is plataform dependent.
Serialization includes Functions to Custom Serialization.
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
SerializationInfo include functions to serialize each member.
BinaryWriter and BinaryReader also contains methods to Save / Load to Byte Array (Stream).
Note that you can create a MemoryStream from a Byte Array or a Byte Array from a MemoryStream.
You can create a method Save and a method New on your structure:
Save(Bw as BinaryWriter)
New (Br as BinaryReader)
Then you select members to Save / Load to Stream -> Byte Array.
Almost all of the answers here use Marshal.StructureToPtr, which might be good for P/Invoke but it is very slow, and doesn't even always represent the actual raw content of the value. #Varscott128's answer is much better but it also contains an explicit byte copying, which is not necessary.
For unmanaged structs (structs without managed references) all you need is to reinterpret the allocated result array so a simple assignment does the trick (works even for huge structs):
.NET (Core) Solution:
If you can use the Unsafe class, then the solution is really easy. The unsafe modifier is required only due to sizeof(T).
public static unsafe byte[] SerializeValueType<T>(in T value) where T : unmanaged
{
byte[] result = new byte[sizeof(T)];
Unsafe.As<byte, T>(ref result[0]) = value;
return result;
}
// Note: Validation is omitted for simplicity
public static T DeserializeValueType<T>(byte[] data) where T : unmanaged
=> return Unsafe.As<byte, T>(ref data[0]);
.NET Framework/Standard Solution:
public static unsafe byte[] SerializeValueType<T>(in T value) where T : unmanaged
{
byte[] result = new byte[sizeof(T)];
fixed (byte* dst = result)
*(T*)dst = value;
return result;
}
// Note: Validation is omitted for simplicity
public static unsafe T DeserializeValueType<T>(byte[] data) where T : unmanaged
{
fixed (byte* src = data)
return *(T*)src;
}
See the complete code with validations here.
Remarks:
The OP's example contains a string, which is a reference type so the solution above cannot be used for that. And if you can't use generic methods for some reason things start to get more complicated, especially for .NET Framework (but non-generic size calculation is a pain also on the Core platform). If performance does not matter, then you can revert to Marshal.SizeOf and StructureToPtr as suggested by several other answers, or feel free to use the BinarySerializer.SerializeValueType method from my library that I linked also for the examples above (NuGet).
This can be done very straightforwardly.
Define your struct explicitly with [StructLayout(LayoutKind.Explicit)]
int size = list.GetLength(0);
IntPtr addr = Marshal.AllocHGlobal(size * sizeof(DataStruct));
DataStruct *ptrBuffer = (DataStruct*)addr;
foreach (DataStruct ds in list)
{
*ptrBuffer = ds;
ptrBuffer += 1;
}
This code can only be written in an unsafe context. You have to free addr when you're done with it.
Marshal.FreeHGlobal(addr);
I've come up with a different approach that could convert any struct without the hassle of fixing length, however the resulting byte array would have a little bit more overhead.
Here is a sample struct:
[StructLayout(LayoutKind.Sequential)]
public class HelloWorld
{
public MyEnum enumvalue;
public string reqtimestamp;
public string resptimestamp;
public string message;
public byte[] rawresp;
}
As you can see, all those structures would require adding the fixed length attributes. Which could often ended up taking up more space than required. Note that the LayoutKind.Sequential is required, as we want reflection to always gives us the same order when pulling for FieldInfo. My inspiration is from TLV Type-Length-Value. Let's have a look at the code:
public static byte[] StructToByteArray<T>(T obj)
{
using (MemoryStream ms = new MemoryStream())
{
FieldInfo[] infos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo info in infos)
{
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream inms = new MemoryStream()) {
bf.Serialize(inms, info.GetValue(obj));
byte[] ba = inms.ToArray();
// for length
ms.Write(BitConverter.GetBytes(ba.Length), 0, sizeof(int));
// for value
ms.Write(ba, 0, ba.Length);
}
}
return ms.ToArray();
}
}
The above function simply uses the BinaryFormatter to serialize the unknown size raw object, and I simply keep track of the size as well and store it inside the output MemoryStream too.
public static void ByteArrayToStruct<T>(byte[] data, out T output)
{
output = (T) Activator.CreateInstance(typeof(T), null);
using (MemoryStream ms = new MemoryStream(data))
{
byte[] ba = null;
FieldInfo[] infos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo info in infos)
{
// for length
ba = new byte[sizeof(int)];
ms.Read(ba, 0, sizeof(int));
// for value
int sz = BitConverter.ToInt32(ba, 0);
ba = new byte[sz];
ms.Read(ba, 0, sz);
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream inms = new MemoryStream(ba))
{
info.SetValue(output, bf.Deserialize(inms));
}
}
}
}
When we want to convert it back to its original struct we simply read the length back and directly dump it back into the BinaryFormatter which in turn dump it back into the struct.
These 2 functions are generic and should work with any struct, I've tested the above code in my C# project where I have a server and a client, connected and communicate via NamedPipeStream and I forward my struct as byte array from one and to another and converted it back.
I believe my approach might be better, since it doesn't fix length on the struct itself and the only overhead is just an int for every fields you have in your struct. There are also some tiny bit overhead inside the byte array generated by BinaryFormatter, but other than that, is not much.
I would take a look at the BinaryReader and BinaryWriter classes. I recently had to serialize data to a byte array (and back) and only found these classes after I'd basically rewritten them myself.
http://msdn.microsoft.com/en-us/library/system.io.binarywriter.aspx
There is a good example on that page too.
Looks like a predefined (C level) structure for some external library. Marshal is your friend. Check:
http://geekswithblogs.net/taylorrich/archive/2006/08/21/88665.aspx
for a starter how to deal with this. Note that you can - with attributes - define things like byte layout and string handling. VERY nice approach, actually.
Neither BinaryFormatter Nor MemoryStream are done for that.
#Abdel Olakara answer donese not work in .net 3.5, should be modified as below:
public static void ByteArrayToStructure<T>(byte[] bytearray, ref T obj)
{
int len = Marshal.SizeOf(obj);
IntPtr i = Marshal.AllocHGlobal(len);
Marshal.Copy(bytearray, 0, i, len);
obj = (T)Marshal.PtrToStructure(i, typeof(T));
Marshal.FreeHGlobal(i);
}
Header header = new Header();
Byte[] headerBytes = new Byte[Marshal.SizeOf(header)];
Marshal.Copy((IntPtr)(&header), headerBytes, 0, headerBytes.Length);
This should do the trick quickly, right?
This example here is only applicable to pure blittable types, e.g., types that can be memcpy'd directly in C.
Example - well known 64-bit struct
[StructLayout(LayoutKind.Sequential)]
public struct Voxel
{
public ushort m_id;
public byte m_red, m_green, m_blue, m_alpha, m_matid, m_custom;
}
Defined exactly like this, the struct will be automatically packed as 64-bit.
Now we can create volume of voxels:
Voxel[,,] voxels = new Voxel[16,16,16];
And save them all to a byte array:
int size = voxels.Length * 8; // Well known size: 64 bits
byte[] saved = new byte[size];
GCHandle h = GCHandle.Alloc(voxels, GCHandleType.Pinned);
Marshal.Copy(h.AddrOfPinnedObject(), saved, 0, size);
h.Free();
// now feel free to save 'saved' to a File / memory stream.
However, since the OP wants to know how to convert the struct itself, our Voxel struct can have following method ToBytes:
byte[] bytes = new byte[8]; // Well known size: 64 bits
GCHandle h = GCHandle.Alloc(this, GCHandleType.Pinned);
Marshal.Copy(hh.AddrOfPinnedObject(), bytes, 0, 8);
h.Free();

C# unsafe value type array to byte array conversions

I use an extension method to convert float arrays into byte arrays:
public static unsafe byte[] ToByteArray(this float[] floatArray, int count)
{
int arrayLength = floatArray.Length > count ? count : floatArray.Length;
byte[] byteArray = new byte[4 * arrayLength];
fixed (float* floatPointer = floatArray)
{
fixed (byte* bytePointer = byteArray)
{
float* read = floatPointer;
float* write = (float*)bytePointer;
for (int i = 0; i < arrayLength; i++)
{
*write++ = *read++;
}
}
}
return byteArray;
}
I understand that an array is a pointer to memory associated with information on the type and number of elements. Also, it seems to me that there is no way of doing a conversion from and to a byte array without copying the data as above.
Have I understood this? Would it even be impossible to write IL to create an array from a pointer, type and length without copying data?
EDIT: Thanks for the answers, I learned some fundamentals and got to try out new tricks!
After initially accepting Davy Landman's answer I found out that while his brilliant StructLayout hack does convert byte arrays into float arrays, it does not work the other way around. To demonstrate:
[StructLayout(LayoutKind.Explicit)]
struct UnionArray
{
[FieldOffset(0)]
public Byte[] Bytes;
[FieldOffset(0)]
public float[] Floats;
}
static void Main(string[] args)
{
// From bytes to floats - works
byte[] bytes = { 0, 1, 2, 4, 8, 16, 32, 64 };
UnionArray arry = new UnionArray { Bytes = bytes };
for (int i = 0; i < arry.Bytes.Length / 4; i++)
Console.WriteLine(arry.Floats[i]);
// From floats to bytes - index out of range
float[] floats = { 0.1f, 0.2f, 0.3f };
arry = new UnionArray { Floats = floats };
for (int i = 0; i < arry.Floats.Length * 4; i++)
Console.WriteLine(arry.Bytes[i]);
}
It seems that the CLR sees both arrays as having the same length. If the struct is created from float data, the byte array's length is just too short.
You can use a really ugly hack to temporary change your array to byte[] using memory manipulation.
This is really fast and efficient as it doesn't require cloning the data and iterating on it.
I tested this hack in both 32 & 64 bit OS, so it should be portable.
The source + sample usage is maintained at https://gist.github.com/1050703 , but for your convenience I'll paste it here as well:
public static unsafe class FastArraySerializer
{
[StructLayout(LayoutKind.Explicit)]
private struct Union
{
[FieldOffset(0)] public byte[] bytes;
[FieldOffset(0)] public float[] floats;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct ArrayHeader
{
public UIntPtr type;
public UIntPtr length;
}
private static readonly UIntPtr BYTE_ARRAY_TYPE;
private static readonly UIntPtr FLOAT_ARRAY_TYPE;
static FastArraySerializer()
{
fixed (void* pBytes = new byte[1])
fixed (void* pFloats = new float[1])
{
BYTE_ARRAY_TYPE = getHeader(pBytes)->type;
FLOAT_ARRAY_TYPE = getHeader(pFloats)->type;
}
}
public static void AsByteArray(this float[] floats, Action<byte[]> action)
{
if (floats.handleNullOrEmptyArray(action))
return;
var union = new Union {floats = floats};
union.floats.toByteArray();
try
{
action(union.bytes);
}
finally
{
union.bytes.toFloatArray();
}
}
public static void AsFloatArray(this byte[] bytes, Action<float[]> action)
{
if (bytes.handleNullOrEmptyArray(action))
return;
var union = new Union {bytes = bytes};
union.bytes.toFloatArray();
try
{
action(union.floats);
}
finally
{
union.floats.toByteArray();
}
}
public static bool handleNullOrEmptyArray<TSrc,TDst>(this TSrc[] array, Action<TDst[]> action)
{
if (array == null)
{
action(null);
return true;
}
if (array.Length == 0)
{
action(new TDst[0]);
return true;
}
return false;
}
private static ArrayHeader* getHeader(void* pBytes)
{
return (ArrayHeader*)pBytes - 1;
}
private static void toFloatArray(this byte[] bytes)
{
fixed (void* pArray = bytes)
{
var pHeader = getHeader(pArray);
pHeader->type = FLOAT_ARRAY_TYPE;
pHeader->length = (UIntPtr)(bytes.Length / sizeof(float));
}
}
private static void toByteArray(this float[] floats)
{
fixed(void* pArray = floats)
{
var pHeader = getHeader(pArray);
pHeader->type = BYTE_ARRAY_TYPE;
pHeader->length = (UIntPtr)(floats.Length * sizeof(float));
}
}
}
And the usage is:
var floats = new float[] {0, 1, 0, 1};
floats.AsByteArray(bytes =>
{
foreach (var b in bytes)
{
Console.WriteLine(b);
}
});
Yes, the type information and data is in the same memory block, so that is impossible unless you overwrite the type information in a float array to fool the system that it's byte array. That would be a really ugly hack, and could easily blow up...
Here's how you can convert the floats without unsafe code if you like:
public static byte[] ToByteArray(this float[] floatArray) {
int len = floatArray.Length * 4;
byte[] byteArray = new byte[len];
int pos = 0;
foreach (float f in floatArray) {
byte[] data = BitConverter.GetBytes(f);
Array.Copy(data, 0, byteArray, pos, 4);
pos += 4;
}
return byteArray;
}
This question is the reverse of What is the fastest way to convert a float[] to a byte[]?.
I've answered with a union kind of hack to skip the whole copying of the data. You could easily reverse this (length = length *sizeof(Double).
I've written something similar for quick conversion between arrays. It's basically an ugly proof-of-concept more than a handsome solution. ;)
public static TDest[] ConvertArray<TSource, TDest>(TSource[] source)
where TSource : struct
where TDest : struct {
if (source == null)
throw new ArgumentNullException("source");
var sourceType = typeof(TSource);
var destType = typeof(TDest);
if (sourceType == typeof(char) || destType == typeof(char))
throw new NotSupportedException(
"Can not convert from/to a char array. Char is special " +
"in a somewhat unknown way (like enums can't be based on " +
"char either), and Marshal.SizeOf returns 1 even when the " +
"values held by a char can be above 255."
);
var sourceByteSize = Buffer.ByteLength(source);
var destTypeSize = Marshal.SizeOf(destType);
if (sourceByteSize % destTypeSize != 0)
throw new Exception(
"The source array is " + sourceByteSize + " bytes, which can " +
"not be transfered to chunks of " + destTypeSize + ", the size " +
"of type " + typeof(TDest).Name + ". Change destination type or " +
"pad the source array with additional values."
);
var destCount = sourceByteSize / destTypeSize;
var destArray = new TDest[destCount];
Buffer.BlockCopy(source, 0, destArray, 0, sourceByteSize);
return destArray;
}
}
public byte[] ToByteArray(object o)
{
int size = Marshal.SizeOf(o);
byte[] buffer = new byte[size];
IntPtr p = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(o, p, false);
Marshal.Copy(p, buffer, 0, size);
}
finally
{
Marshal.FreeHGlobal(p);
}
return buffer;
}
this may help you to convert an object to a byte array.
You should check my answer to a similar question: What is the fastest way to convert a float[] to a byte[]?.
In it you'll find portable code (32/64 bit compatible) to let you view a float array as a byte array or vice-versa, without copying the data. It's the fastest way that I know of to do such thing.
If you're just interested in the code, it's maintained at https://gist.github.com/1050703 .
Well - if you still interested in that hack - check out this modified code - it works like a charm and costs ~0 time, but it may not work in future since it's a hack allowing to gain full access to the whole process address space without trust requirements and unsafe marks.
[StructLayout(LayoutKind.Explicit)]
struct ArrayConvert
{
public static byte[] GetBytes(float[] floats)
{
ArrayConvert ar = new ArrayConvert();
ar.floats = floats;
ar.length.val = floats.Length * 4;
return ar.bytes;
}
public static float[] GetFloats(byte[] bytes)
{
ArrayConvert ar = new ArrayConvert();
ar.bytes = bytes;
ar.length.val = bytes.Length / 4;
return ar.floats;
}
public static byte[] GetTop4BytesFrom(object obj)
{
ArrayConvert ar = new ArrayConvert();
ar.obj = obj;
return new byte[]
{
ar.top4bytes.b0,
ar.top4bytes.b1,
ar.top4bytes.b2,
ar.top4bytes.b3
};
}
public static byte[] GetBytesFrom(object obj, int size)
{
ArrayConvert ar = new ArrayConvert();
ar.obj = obj;
ar.length.val = size;
return ar.bytes;
}
class ArrayLength
{
public int val;
}
class Top4Bytes
{
public byte b0;
public byte b1;
public byte b2;
public byte b3;
}
[FieldOffset(0)]
private Byte[] bytes;
[FieldOffset(0)]
private object obj;
[FieldOffset(0)]
private float[] floats;
[FieldOffset(0)]
private ArrayLength length;
[FieldOffset(0)]
private Top4Bytes top4bytes;
}

Categories