/// <summary> /// Serializes this storage object into a byte array /// </summary> /// <param name="IncludeHeader">Whether or not to include the storage header in the output</param> /// <returns>A serialized byte array representation of all stored value entries</returns> public byte[] Serialize(bool IncludeHeader = true) { // Lock our dictionary to prevent race conditions lock (Entries) { // Begin by creating an empty output buffer byte[] Output = new byte[0]; // Write header to output buffer if specified if (IncludeHeader) { // Add signatures and version Output = Output.AppendBytes(STORAGE_SIGNATURE_A); Output = Output.AppendBytes(STORAGE_SIGNATURE_B); Output = Output.AppendBytes(STORAGE_VERSION); } // Add the number of entries our entry dictionary contains as a varint Output = Output.AppendBytes(PackP2pVarInt(Entries.Count)); // Iterate over and serialize each entry into our output buffer foreach (var Entry in Entries) { // Serialize entry object byte[] ObjectBytes = SerializeObject(Entry); // Append to output buffer Output = Output.AppendBytes(ObjectBytes); } // Return output array return(Output); } }
// Deserialized a storage entry from a byte buffer private byte[] DeserializeEntry(byte[] Data) { // Buffer is empty if (Data.Length == 0) { return(Data); } // Get entry name int NameLength = ByteArrayToInteger <byte>(Data, 0); if (NameLength < 1 || NameLength > MAX_STRING_LENGTH) { throw new Exception("Name size exceeds allowed string bounds"); } string Name = System.Text.Encoding.UTF8.GetString(Data, 1, NameLength); Data = Data.SubBytes(NameLength + 1, Data.Length - NameLength - 1); // Get object type int ValueType = Data.SubBytes(0, 1)[0]; // Type is a serializable type if (ValueType > 0 || ValueType < 14) { // Get serialization type SerializationType Type = (SerializationType)ValueType; Data = Data.SubBytes(1, Data.Length - 1); // Deserialize object based on type switch (Type) { #region Integers case SerializationType.LONG: Entries.Add(Name, ByteArrayToInteger <long>(Data)); return(Data.SubBytes(8, Data.Length - 8)); case SerializationType.INT: Entries.Add(Name, ByteArrayToInteger <int>(Data)); return(Data.SubBytes(4, Data.Length - 4)); case SerializationType.SHORT: Entries.Add(Name, ByteArrayToInteger <short>(Data)); return(Data.SubBytes(2, Data.Length - 2)); case SerializationType.SBYTE: Entries.Add(Name, ByteArrayToInteger <sbyte>(Data)); return(Data.SubBytes(1, Data.Length - 1)); case SerializationType.ULONG: Entries.Add(Name, ByteArrayToInteger <ulong>(Data)); return(Data.SubBytes(8, Data.Length - 8)); case SerializationType.UINT: Entries.Add(Name, ByteArrayToInteger <uint>(Data)); return(Data.SubBytes(4, Data.Length - 4)); case SerializationType.USHORT: Entries.Add(Name, ByteArrayToInteger <ushort>(Data)); return(Data.SubBytes(2, Data.Length - 2)); case SerializationType.BYTE: Entries.Add(Name, ByteArrayToInteger <byte>(Data)); return(Data.SubBytes(1, Data.Length - 1)); case SerializationType.DOUBLE: Entries.Add(Name, ByteArrayToInteger <double>(Data)); return(Data.SubBytes(8, Data.Length - 8)); case SerializationType.BOOL: Entries.Add(Name, Convert.ToBoolean(ByteArrayToInteger <byte>(Data))); return(Data.SubBytes(1, Data.Length - 1)); #endregion #region Miscellaneous case SerializationType.STRING: int Length = UnpackP2pVarInt <int>(Data, 0, out int Offset); Entries.Add(Name, ByteArrayToHexString(Data.SubBytes(Offset, Length))); return(Data.SubBytes(Offset + Length, Data.Length - Offset - Length)); #endregion #region Not Implemented case SerializationType.OBJECT: PortableStorage Storage = new PortableStorage(Data, out Data, false); Entries.Add(Name, Storage.Entries); return(Data); case SerializationType.OBJECTARRAY: return(new byte[0]); default: return(new byte[0]); #endregion } } // Non-serializable type, treat as a single long hex string else { // TODO - DEBUG CODE Entries.Add(Name, ByteArrayToHexString(Data)); return(new byte[0]); } }
// Serializes an object to a byte array private static byte[] SerializeObject(dynamic Value) { // Get object's type var Type = GetType(Value); byte[] Output = new[] { (byte)Type }; // Serialize object based on type switch (Type) { #region Integers case SerializationType.LONG: Output = Output.AppendInteger((long)Value); break; case SerializationType.INT: Output = Output.AppendInteger((int)Value); break; case SerializationType.SHORT: Output = Output.AppendInteger((short)Value); break; case SerializationType.SBYTE: Output = Output.AppendInteger((sbyte)Value); break; case SerializationType.ULONG: Output = Output.AppendInteger((ulong)Value); break; case SerializationType.UINT: Output = Output.AppendInteger((uint)Value); break; case SerializationType.USHORT: Output = Output.AppendInteger((ushort)Value); break; case SerializationType.BYTE: Output = Output.AppendInteger((byte)Value); break; case SerializationType.DOUBLE: Output = Output.AppendInteger((double)Value); break; case SerializationType.BOOL: Output = Output.AppendInteger((bool)Value ? (byte)0x01 : (byte)0x00); break; #endregion #region Others case SerializationType.STRING: // String size exceeds maximum length, default to nothing if (((string)Value).Length > MAX_STRING_LENGTH) { throw new ArgumentOutOfRangeException("Entry string was too long to serialize"); } // Append string length as a varint Output = Output.AppendBytes(PackP2pVarInt(((string)Value).Length)); // Append string bytes Output = Output.AppendString((string)Value); break; case SerializationType.BYTEARRAY: // Set serialization type to string Output = new byte[] { (byte)SerializationType.STRING }; // Byte size exceeds maximum length, default to nothing if (((byte[])Value).Length > MAX_STRING_LENGTH) { throw new ArgumentOutOfRangeException("Entry byte array was too long to serialize"); } // Append string length as a varint Output = Output.AppendBytes(PackP2pVarInt(((byte[])Value).Length)); // Append string bytes Output = Output.AppendBytes((byte[])Value); break; #endregion #region Not Implemented case SerializationType.OBJECT: // Object is a dictionary Type ValueType = Value.GetType(); if (ValueType.IsGenericType && ValueType.GetGenericTypeDefinition() == typeof(Dictionary <,>)) { // Create a portable storage from dictionary PortableStorage Storage = (Dictionary <string, dynamic>)Value; // Append bytes Output = Output.AppendBytes(Storage.Serialize(false)); break; } else { throw new NotImplementedException(); } case SerializationType.OBJECTARRAY: throw new NotImplementedException(); default: return(new byte[0]); #endregion } // Return resulting output buffer return(Output); }