/// <summary>
        /// Write a given object to stream
        /// </summary>
        /// <param name="obj">Object to write</param>
        /// <param name="ms">Stream to write to</param>
        public static void Serialize(object obj, SuperStream ms)
        {
            if (typeof(IStreamDataFinalizer).IsAssignableFrom(obj.GetType()))
            {
                IStreamDataFinalizer fin = (IStreamDataFinalizer)obj;
                fin.OnSerialize();
            }
            var properties = StreamDataInfo.GetProperties(obj.GetType());

            // Parse spell arguments
            foreach (var pi in properties)
            {
                dynamic value             = pi.PropertyInfo.SafeGetValue(obj, null);
                StreamDataParserTask task = new StreamDataParserTask(ms, pi.ReadType, pi.DataType, pi.Attributes);

                if (pi.IsCollection)
                {
                    // Find actual length of collection.
                    ulong length;
                    if (pi.IsArray)
                    {
                        length = (ulong)value.Length;
                    }
                    else if (pi.IsList)
                    {
                        length = (ulong)value.Count;
                    }
                    else
                    {
                        throw new Exception("Property is collection, but not array nor list.");
                    }

                    // Write length, and return written length. (Entries= will override length of collection if set)
                    length = pi.WriteContentLength(ms, length);

                    dynamic enumerable = pi.PropertyInfo.SafeGetValue(obj, null);
                    ulong   count      = 0;
                    foreach (var entry in enumerable)
                    {
                        // Make sure we do not write more entries than we've declared
                        count++;
                        if (count > length)
                        {
                            throw new Exception("Collection contains more items than ");
                        }

                        // Write enry.
                        if (!WriteParserData(task, entry))
                        {
                            throw new Exception();
                        }
                    }
                    continue;
                }
                else
                {
                    if (!WriteParserData(task, value))
                    {
                        throw new Exception();
                    }
                }
            }
        }
        /// <summary>
        /// Populates an existing object with data from stream.
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="ms"></param>
        /// <returns></returns>
        public static object Populate(object obj, SuperStream ms)
        {
            var     properties        = StreamDataInfo.GetProperties(obj.GetType());
            bool    gracefulStopAtEOF = obj.GetType().GetAttribute <StreamDataGracefulEofAttribute>() != null;
            dynamic value;

            // Parse spell arguments
            foreach (var pi in properties)
            {
                if (gracefulStopAtEOF && ms.EOF)
                {
                    break;
                }
                StreamDataParserTask task = new StreamDataParserTask(ms, pi.ReadType, pi.DataType, pi.Attributes);

                if (pi.IsCollection)
                {
                    var entries = pi.ReadContentLength(ms);

                    if (pi.IsArray)
                    {
                        var arr = new ArrayList((int)Math.Min(int.MaxValue, entries));
                        for (ulong i = 0; i < entries; i++)
                        {
                            if (!GetParserData(task, out value))
                            {
                                throw new Exception();
                            }
                            arr.Add(value);
                        }

                        var arr2 = arr.ToArray(pi.ReadType);
                        pi.PropertyInfo.SafeSetValue(obj, arr2, null);
                        continue;
                    }
                    else if (pi.IsList)
                    {
                        dynamic list = Activator.CreateInstance(
                            typeof(List <>).MakeGenericType(pi.DataType),
                            (int)Math.Min(int.MaxValue, entries));

                        for (ulong i = 0; i < entries; i++)
                        {
                            if (!GetParserData(task, out value))
                            {
                                throw new Exception();
                            }
                            list.Add(value);
                        }
                        pi.PropertyInfo.SafeSetValue(obj, (object)list, null);
                    }
                }
                else
                {
                    if (!GetParserData(task, out value))
                    {
                        throw new Exception();
                    }
                    pi.PropertyInfo.SafeSetValue(obj, (object)value, null);
                    continue;
                }
            }
            if (typeof(IStreamDataFinalizer).IsAssignableFrom(obj.GetType()))
            {
                IStreamDataFinalizer fin = (IStreamDataFinalizer)obj;
                fin.OnDeserialize();
            }
            return(obj);
        }