using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace IFS { /// /// Custom attribute allowing specification of Word (16-bit) alignment /// of a given field. (Alignment is byte-oriented by default). /// [System.AttributeUsage(System.AttributeTargets.Field)] public class WordAligned : System.Attribute { public WordAligned() { } } /// /// Custom attribute allowing static specification of size (in elements) of array /// fields. Used during deserialization. /// public class ArrayLength : System.Attribute { public ArrayLength(int i) { Length = i; } public int Length; } public static class Serializer { /// /// Deserializes the specified byte array into the specified object. /// /// /// /// public static object Deserialize(byte[] data, Type t) { // // We support serialization of structs containing only: // - byte // - ushort // - short // - int // - uint // - BCPLString // - string (MUST be last field in struct, if present) // - byte[] // // Struct fields are serialized in the order they are defined in the struct. Only Public instance fields are considered. // If any unsupported fields are present in the considered field types, an exception will be thrown. // MemoryStream ms = new MemoryStream(data); System.Reflection.FieldInfo[] info = t.GetFields(BindingFlags.Public | BindingFlags.Instance); object o = Activator.CreateInstance(t); for (int i = 0; i < info.Length; i++) { // Check alignment of the field; if word aligned we need to ensure proper positioning of the stream. if (IsWordAligned(info[i])) { if ((ms.Position % 2) != 0) { // Eat up a padding byte ms.ReadByte(); } } // Now read in the appropriate type. switch (info[i].FieldType.Name) { case "Byte": info[i].SetValue(o, (byte)ms.ReadByte()); break; case "UInt16": { info[i].SetValue(o, Helpers.ReadUShort(ms)); } break; case "Int16": { info[i].SetValue(o, (short)Helpers.ReadUShort(ms)); } break; case "UInt32": { info[i].SetValue(o, Helpers.ReadUInt(ms)); } break; case "Int32": { info[i].SetValue(o, (int)Helpers.ReadUInt(ms)); } break; case "BCPLString": { info[i].SetValue(o, new BCPLString(ms)); } break; case "Byte[]": { // The field MUST be annotated with a length value. int length = GetArrayLength(info[i]); if (length == -1) { throw new InvalidOperationException("Byte arrays must be annotated with an ArrayLength attribute to be deserialized into."); } byte[] value = new byte[length]; ms.Read(value, 0, value.Length); info[i].SetValue(o, value); } break; case "String": { // The field MUST be the last in the struct. if (i != info.Length - 1) { throw new InvalidOperationException("Non-BCPL strings must be the last field in the struct to be deserialized."); } StringBuilder sb = new StringBuilder((int)(ms.Length - ms.Position)); while (ms.Position != ms.Length) { sb.Append((char)ms.ReadByte()); } info[i].SetValue(o, sb.ToString()); } break; default: throw new InvalidOperationException(String.Format("Type {0} is unsupported for deserialization.", info[i].FieldType.Name)); } } return o; } /// /// Serialize the object (if supported) to an array. /// /// /// public static byte[] Serialize(object o) { MemoryStream ms = new MemoryStream(); // // We support serialization of structs containing only: // - byte // - ushort // - short // - int // - uint // - string // - byte[] // - BCPLString // // Struct fields are serialized in the order they are defined in the struct. Only Public instance fields are considered. // If any unsupported fields are present in the considered field types, an exception will be thrown. // System.Reflection.FieldInfo[] info = o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); for(int i=0;i 0 && length != value.Length) { throw new InvalidOperationException("Array size does not match the size required by the ArraySize annotation."); } ms.Write(value, 0, value.Length); } break; case "String": { string value = (string)(info[i].GetValue(o)); byte[] stringArray = Helpers.StringToArray(value); ms.Write(stringArray, 0, stringArray.Length); } break; default: throw new InvalidOperationException(String.Format("Type {0} is unsupported for serialization.", info[i].FieldType.Name)); } } return ms.ToArray(); } private static void SwapBytes(byte[] data) { for (int i = 0; i < data.Length; i += 2) { byte t = data[i]; data[i] = data[i + 1]; data[i + 1] = t; } } private static bool IsWordAligned(FieldInfo field) { foreach(CustomAttributeData attribute in field.CustomAttributes) { if (attribute.AttributeType == typeof(WordAligned)) { return true; } } return false; } private static int GetArrayLength(FieldInfo field) { foreach (Attribute attribute in System.Attribute.GetCustomAttributes(field)) { if (attribute is ArrayLength) { return ((ArrayLength)attribute).Length; } } return -1; } } }