Skip to content

Instantly share code, notes, and snippets.

@v0l
Last active June 24, 2016 15:57
Show Gist options
  • Save v0l/ed181f085ff1b433135ac194ef46e0c8 to your computer and use it in GitHub Desktop.
Save v0l/ed181f085ff1b433135ac194ef46e0c8 to your computer and use it in GitHub Desktop.
A c# Binary serializer using attribute flags, usefull if you need to do network communication with C++ applications that copy structs directly from memory.
[NPPSerializer(Size = 28)]
public struct npkt_t
{
[NPPMember(IsBigEndian = true)]
public uint p_type;
[NPPMember(IsBigEndian = true)]
public uint p_length;
[NPPMember(IsBigEndian = true)]
public uint p_magic;
[NPPMember(IsBigEndian = true)]
public uint p_version;
[NPPMember(IsBigEndian = true)]
public uint p_seq;
[NPPMember(IsBigEndian = true)]
public uint p_opcode;
[NPPMember(IsBigEndian = true)]
public uint p_session_id;
}
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
namespace npid
{
[AttributeUsage(AttributeTargets.Struct)]
public class NPPSerializer : Attribute
{
private static Exception _noNPPAttribute = new Exception("NPPSerializer attributes not found");
public uint Offset { get; set; }
public uint Size { get; set; }
public static int SizeOf(Type t)
{
int ret = 0;
if (t.IsValueType)
{
var attrib = t.GetCustomAttribute<NPPSerializer>();
if (attrib != null)
{
ret = (int)attrib.Size;
}
else
{
ret = Marshal.SizeOf(t);
}
}
else
{
ret = 0;
}
return ret;
}
public static int SizeOf(FieldInfo i)
{
int ret = 0;
var attrib = i.GetCustomAttribute<NPPMember>();
if (attrib != null && attrib.Size != 0)
{
ret = (int)attrib.Size;
}
else
{
ret = Marshal.SizeOf(i.FieldType);
}
return ret;
}
public static byte[] Serialize<T>(T obj) where T : struct
{
byte[] ret = null;
var attrib = typeof(T).GetCustomAttribute<NPPSerializer>();
if (attrib != null)
{
if (attrib.Size != 0)
{
ret = new byte[attrib.Size];
int off = 0;
foreach (var f in typeof(T).GetFields())
{
var mem = f.GetCustomAttribute<NPPMember>();
if (mem != null)
{
object val = f.GetValue(obj);
int size = NPPSerializer.SizeOf(f);
byte[] data = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(data.Length);
Marshal.StructureToPtr(val, ptr, false);
Marshal.Copy(ptr, data, 0, data.Length);
Marshal.FreeHGlobal(ptr);
if (mem.Size == 0)
{
mem.Size = size;
}
if (mem.Size < data.Length)
{
throw new Exception("Data size is longer than defined size");
}
if (mem.IsBigEndian && BitConverter.IsLittleEndian) //reverse data if IsLittleEndian system
{
data = data.Reverse().ToArray();
}
if (mem.Offset == 0) //Move offset to next position in array
{
mem.Offset = off;
}
Array.Copy(data, 0, ret, mem.Offset, data.Length);
data = null;
off += mem.Size;
}
}
}
else
{
throw new Exception("Size is not specified, please set the size");
}
}
else
{
throw _noNPPAttribute;
}
return ret;
}
public static T Deserialize<T>(byte[] data) where T : struct
{
T ret = default(T);
var attrib = typeof(T).GetCustomAttribute<NPPSerializer>();
if (attrib != null)
{
if (data.Length == attrib.Size)
{
int off = 0;
foreach (var f in typeof(T).GetFields())
{
var mem = f.GetCustomAttribute<NPPMember>();
if (mem != null)
{
var size = NPPSerializer.SizeOf(f);
var ptr = Marshal.AllocHGlobal(size);
if (mem.Offset == 0)
{
mem.Offset = off;
}
byte[] indata = new byte[size];
Array.Copy(data, mem.Offset, indata, 0, size);
off += size;
if (mem.IsBigEndian && BitConverter.IsLittleEndian)
{
indata = indata.Reverse().ToArray();
}
Marshal.Copy(indata, 0, ptr, size);
if (mem.Size == 0)
{
mem.Size = size;
}
if (mem.Size != size)
{
throw new Exception("Data size does not match");
}
if (f.FieldType.FullName == "System.String") //strings are weird
{
string obj = Encoding.UTF8.GetString(indata);
obj = obj.Substring(0, obj.IndexOf("\0"));
f.SetValueForValueType<T>(ref ret, obj);
}
else
{
var obj = Marshal.PtrToStructure(ptr, f.FieldType);
f.SetValueForValueType<T>(ref ret, obj);
}
//cleanup
indata = null;
Marshal.FreeHGlobal(ptr);
}
}
}
else
{
throw new Exception(string.Format("Size does not match data({0}) != struct({1})", data.Length, attrib.Size));
}
}
else
{
throw _noNPPAttribute;
}
return ret;
}
}
[AttributeUsage(AttributeTargets.Field)]
public class NPPMember : Attribute
{
public int Size { get; set; }
public int Offset { get; set; }
public bool IsBigEndian { get; set; }
}
[CLSCompliant(true)]
static class Hlp
{
public static void SetValueForValueType<T>(this FieldInfo field, ref T item, object value) where T : struct
{
field.SetValueDirect(__makeref(item), value);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment