Last active
June 24, 2016 15:57
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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