private PacketDefinition(IFieldReader reader) { m_TypeName = reader.ReadString(); m_Version = reader.ReadInt32(); int fieldCount = reader.ReadInt32(); for (int i = 0; i < fieldCount; i++) { string fieldName = reader.ReadString(); FieldType fieldType = (FieldType)reader.ReadInt32(); m_Fields.Add(new FieldDefinition(fieldName, fieldType)); } // Handle the possiblity that a Packet aggregates lower level packets int subPacketCount = reader.ReadInt32(); m_SubPackets = new List <PacketDefinition>(); for (int i = 0; i < subPacketCount; i++) { // We need to call the static ReadPacketDefinition(reader) in order to // read and process the cacheable, version, and dynamic name fields which // also exist for each subPacket definition. Fixed as part of Case #165 PacketDefinition subPacket = ReadPacketDefinition(reader); m_SubPackets.Add(subPacket); } }
/// <summary> /// Returns a PacketDefinition from the stream (including nested PacketDefinition /// objects for cases in which an IPacket is subclassed and has serialized state /// at multiple levels). /// </summary> /// <param name="reader">Stream to read data from</param> /// <returns>PacketDefinition (including nested definitions for subclassed packets)</returns> public static PacketDefinition ReadPacketDefinition(IFieldReader reader) { bool cachedPacket = reader.ReadBool(); int nestingDepth = reader.ReadInt32(); if (nestingDepth < 1) { throw new GibraltarException(string.Format(CultureInfo.InvariantCulture, "While reading the definition of the next packet, the number of types in the definition was read as {0} which is less than 1.", nestingDepth)); } string dynamicTypeName = reader.ReadString(); PacketDefinition[] definitions = new PacketDefinition[nestingDepth]; for (int i = 0; i < nestingDepth; i++) { definitions[i] = new PacketDefinition(reader); if (i > 0) { definitions[i].m_BasePacket = definitions[i - 1]; } } PacketDefinition topLevelDefinition = definitions[nestingDepth - 1]; topLevelDefinition.m_IsCachable = cachedPacket; topLevelDefinition.m_DynamicTypeName = dynamicTypeName; return(topLevelDefinition); }
public void SetField(string fieldName, object value) { if (string.IsNullOrEmpty(fieldName)) { throw new ArgumentNullException(nameof(fieldName)); } if (m_ReadOnly) { throw new InvalidOperationException("The packet has been deserialized and is read-only"); } //find the field index for the field name FieldDefinition fieldDefinition = m_Definition.Fields[fieldName]; int fieldIndex = m_Definition.Fields.IndexOf(fieldDefinition); //now we need to check the value for sanity. if (value != null) { FieldType valueType = PacketDefinition.GetSerializableType(value.GetType()); if (fieldDefinition.IsCompatible(valueType) == false) { throw new ArgumentException("The provided value's type doesn't match the definition for the field.", nameof(value)); } } //now that we know we're of the right type, store it away. m_Values[fieldIndex] = value; }
/// <summary> /// Create a storage summary instance referencing a particualr PacketDefinition /// </summary> public PacketTypeStorageSummary(PacketDefinition packetDefinition) { QualifiedTypeName = packetDefinition.QualifiedTypeName; TypeName = packetDefinition.TypeName; PacketCount = packetDefinition.PacketCount; PacketSize = packetDefinition.PacketSize; }
/// <summary> /// Read and return the next IPacket from the stream /// </summary> public IPacket ReadPacket(Stream packetStream) { m_Reader.ReplaceStream(packetStream); PacketDefinition definition; int typeIndex = (int)m_Reader.ReadUInt32(); if (typeIndex >= m_cachedTypes.Count) { definition = PacketDefinition.ReadPacketDefinition(m_Reader); if (string.IsNullOrEmpty(definition.TypeName)) { //we're hosed... we won't be able to parse this packet. throw new GibraltarSerializationException("The type name of the definition is null, which can't be correct. The packet stream is corrupted.", true); } m_cachedTypes.Add(definition); m_cachedTypes.Commit(); } else { definition = m_cachedTypes[typeIndex]; } definition.PacketCount++; definition.PacketSize += packetStream.Length; IPacketFactory factory = m_PacketFactory.GetPacketFactory(definition.TypeName); IPacket packet = factory.CreatePacket(definition, m_Reader); return(packet); }
public PacketDefinition(string typeName, int version, bool canHaveRequiredPackets) { m_TypeName = typeName; m_Version = version; m_BasePacket = null; m_CanHaveRequiredPackets = canHaveRequiredPackets; m_SubPackets = new List <PacketDefinition>(); }
/// <summary> /// Read and return the next IPacket from the stream /// </summary> public IPacket Read() { #if ADD_GUARD_BYTES byte[] preamble = new byte[4]; m_Stream.Read(preamble, 0, 4); //now compare it against the preamble. CompareArrays(preamble, PacketWriter.s_PreambleGuardPattern); #endif int packetSize = (int)m_Reader.ReadUInt64(); //if the packet size is less than one, that's obviously wrong if (packetSize < 1) { throw new GibraltarSerializationException("The size of the next packet is smaller than 1 byte or negative, which can't be correct. The packet stream is corrupted.", true); } // TODO: There's got to be a more efficient way to get this done byte[] buffer = new byte[packetSize]; m_Stream.Read(buffer, 0, packetSize); IFieldReader bufferReader = new FieldReader(new MemoryStream(buffer), m_Reader.Strings, m_MajorVersion, m_MinorVersion); PacketDefinition definition; int typeIndex = (int)bufferReader.ReadUInt32(); if (typeIndex >= m_cachedTypes.Count) { definition = PacketDefinition.ReadPacketDefinition(bufferReader); if (string.IsNullOrEmpty(definition.TypeName)) { //we're hosed... we won't be able to parse this packet. throw new GibraltarSerializationException("The type name of the definition is null, which can't be correct. The packet stream is corrupted.", true); } m_cachedTypes.Add(definition); m_cachedTypes.Commit(); } else { definition = m_cachedTypes[typeIndex]; } IPacketFactory factory = m_PacketFactory.GetPacketFactory(definition.TypeName); IPacket packet = factory.CreatePacket(definition, bufferReader); //we used to populate a packet cache here, but a cached packet should be read just once - it shouldn't be in the stream //(and I changed PacketWriter to enforce that) #if ADD_GUARD_BYTES byte[] postamble = new byte[4]; m_Stream.Read(postamble, 0, 4); //now compare it against the preamble. CompareArrays(postamble, PacketWriter.s_PostambleGuardPattern); #endif return(packet); }
/// <summary> /// Write an object to the stream as its serializable type /// </summary> /// <param name="value">The object (or boxed integral value) to write.</param> public void Write(object value) { //but what type are we going to use? FieldType serializedType = PacketDefinition.GetSerializableType(value.GetType()); Write((Int32)serializedType); // cast to Int32 to match ReadFieldType() Write(value, serializedType); }
/// <summary> /// Compare this PacketDefinition to another to verify that they are equivalent /// for purposes of order-dependant field deserialization. /// </summary> public bool Equals(PacketDefinition other) { // Verify that base packets are equivalent if (BasePacket == null) { if (other.BasePacket != null) { return(false); } } else { if (other.BasePacket == null) { return(false); } if (!BasePacket.Equals(other.BasePacket)) { return(false); } } // Verify that basic characteristics are equivalent if (TypeName != other.TypeName) { return(false); } if (Version != other.Version) { return(false); } if (Fields.Count != other.Fields.Count) { return(false); } // Verify that all fields are equivalent for (int i = 0; i < Fields.Count; i++) { if (Fields[i].Name != other.Fields[i].Name) { return(false); } if (Fields[i].FieldType != other.Fields[i].FieldType) { return(false); } } return(true); }
/// <summary> /// Write the data needed to serialize the state of the packet /// </summary> /// <param name="packet">Object to be serialized, must implement IPacket</param> public void Write(IPacket packet) { //Before we do anything - is this a cached packet that's already been written out? ICachedPacket cachedPacket = packet as ICachedPacket; if (cachedPacket != null) { //it is cacheable - is it in the cache? if (m_PacketCache.Contains(cachedPacket)) { //good to go, we're done. return; } } //First, we need to find out if there are any packets this guy depends on. If there are, //they have to be serialized out first. They may have been - they could be cached. //to do this, we'll need to get the definition. PacketDefinition previewDefinition; int previewTypeIndex = m_CachedTypes.IndexOf(packet); if (previewTypeIndex < 0) { //we're going to get the definition, BUT we're not going to cache it yet. //This is because we recurse on our self if there are required packets, and if one of those //packets is our same type, IT has to write out the definition so that it's on the stream //before the packet itself. previewDefinition = PacketDefinition.CreatePacketDefinition(packet); } else { previewDefinition = m_CachedTypes[previewTypeIndex]; } Dictionary <IPacket, IPacket> requiredPackets = previewDefinition.GetRequiredPackets(packet); foreach (IPacket requiredPacket in requiredPackets.Values) { Write(requiredPacket); //this will handle if it's a cached packet and shouldn't be written out. } //Begin our "transactional" phase try { // This routine is written to either write a complete packet, or to write nothing. // As we build up the packet, we will write to a MemoryStream. Only after we've // built up the complete packet will we write it to the actual stream. m_Buffer.SetLength(0); m_Buffer.Position = 0; // The first time a packet type is written, we send along a packet definition PacketDefinition definition; int typeIndex = m_CachedTypes.IndexOf(packet); if (typeIndex < 0) { // Record that we've seen this type so we don't bother sending the PacketDefinition again definition = PacketDefinition.CreatePacketDefinition(packet); typeIndex = m_CachedTypes.Count; m_CachedTypes.Add(definition); // Each packet always starts with a packet type index. And the first time a new // index is used, it is followed by the packet definition. m_BufferWriter.Write((UInt32)typeIndex); definition.WriteDefinition(m_BufferWriter); } else { // If this type has been written before, just send the type index m_BufferWriter.Write((UInt32)typeIndex); definition = m_CachedTypes[typeIndex]; } // if it's cacheable then we need to add it to our packet cache before we write it out if (definition.IsCachable) { // In the case of an ICachedPacket, we need to add it to the cache. // we'd have already bailed if it was in there. // Note: Use previous cast for efficiency, but we must recast here *if* packet gets reassigned above // Currently it does not get reassigned, so cachedPacket which we cast packet into above is still valid. m_PacketCache.AddOrGet(cachedPacket); } //Finally, and it really is a long journey, we ask the definition to write out //the individual fields for the packet definition.WriteFields(packet, m_BufferWriter); } catch (Exception ex) { GC.KeepAlive(ex); Rollback(); throw; } #if ADD_GUARD_BYTES m_Stream.Write(s_PreambleGuardPattern, 0, s_PreambleGuardPattern.Length); #endif // Write the data to the stream preceded by the length of this packet // NOTE: The logic below is careful to ensure that the length and payload is written in one call // This is necessary to ensure that the GZipStream writes the whole packet in edge cases // of writing the very last packet as an application is exiting. var payloadLength = (int)m_Buffer.Position; // get the actual length of the payload MemoryStream encodedLength = FieldWriter.WriteLength(payloadLength); var lengthLength = (int)encodedLength.Length; var packetBytes = new byte[lengthLength + payloadLength]; encodedLength.Position = 0; // reset the position in preparation to read the data back encodedLength.Read(packetBytes, 0, lengthLength); m_Buffer.Position = 0; // reset the position in preparation to read the data back m_Buffer.Read(packetBytes, lengthLength, payloadLength); m_Stream.Write(packetBytes, 0, packetBytes.Length); #if ADD_GUARD_BYTES m_Stream.Write(s_PostambleGuardPattern, 0, s_PostambleGuardPattern.Length); #endif Commit(); }
/// <summary> /// Read back the field values for the current packet. /// </summary> /// <param name="definition">The definition that was used to persist the packet.</param> /// <param name="packet">The serialized packet to read data from</param> void IPacket.ReadFields(PacketDefinition definition, SerializedPacket packet) { throw new System.NotImplementedException(); }
/// <summary> /// Read any packet based solely on its PacketDefinition /// </summary> /// <param name="definition">PacketDefinition describing the next packet in the stream</param> /// <param name="reader">Data stream to be read</param> public GenericPacket(PacketDefinition definition, IFieldReader reader) { if (definition.BasePacket != null) { m_BasePacket = new GenericPacket(definition.BasePacket, reader); } m_Definition = definition; m_FieldValues = new object[definition.Fields.Count]; for (int index = 0; index < definition.Fields.Count; index++) { switch (definition.Fields[index].FieldType) { case FieldType.Bool: m_FieldValues[index] = reader.ReadBool(); break; case FieldType.BoolArray: m_FieldValues[index] = reader.ReadBoolArray(); break; case FieldType.String: m_FieldValues[index] = reader.ReadString(); break; case FieldType.StringArray: m_FieldValues[index] = reader.ReadStringArray(); break; case FieldType.Int32: m_FieldValues[index] = reader.ReadInt32(); break; case FieldType.Int32Array: m_FieldValues[index] = reader.ReadInt32Array(); break; case FieldType.Int64: m_FieldValues[index] = reader.ReadInt64(); break; case FieldType.Int64Array: m_FieldValues[index] = reader.ReadInt64Array(); break; case FieldType.UInt32: m_FieldValues[index] = reader.ReadUInt32(); break; case FieldType.UInt32Array: m_FieldValues[index] = reader.ReadUInt32Array(); break; case FieldType.UInt64: m_FieldValues[index] = reader.ReadUInt64(); break; case FieldType.UInt64Array: m_FieldValues[index] = reader.ReadUInt64Array(); break; case FieldType.Double: m_FieldValues[index] = reader.ReadDouble(); break; case FieldType.DoubleArray: m_FieldValues[index] = reader.ReadDoubleArray(); break; case FieldType.TimeSpan: m_FieldValues[index] = reader.ReadTimeSpan(); break; case FieldType.TimeSpanArray: m_FieldValues[index] = reader.ReadTimeSpanArray(); break; case FieldType.DateTime: m_FieldValues[index] = reader.ReadDateTime(); break; case FieldType.DateTimeArray: m_FieldValues[index] = reader.ReadDateTimeArray(); break; case FieldType.Guid: m_FieldValues[index] = reader.ReadGuid(); break; case FieldType.GuidArray: m_FieldValues[index] = reader.ReadGuidArray(); break; case FieldType.DateTimeOffset: m_FieldValues[index] = reader.ReadDateTimeOffset(); break; case FieldType.DateTimeOffsetArray: m_FieldValues[index] = reader.ReadDateTimeOffsetArray(); break; default: #if DEBUG if (Debugger.IsAttached) { Debugger.Break(); } #endif throw new InvalidOperationException(string.Format("The field type {0} is unknown so we can't deserialize the packet ", definition.Fields[index].FieldType)); } } }
/// <summary> /// Write out all of the fields for the current packet /// </summary> /// <param name="definition">The definition that was used to persist the packet.</param> /// <param name="packet">The serialized packet to populate with data</param> void IPacket.WriteFields(PacketDefinition definition, SerializedPacket packet) { if (m_BasePacket != null) { ((IPacket)m_BasePacket).WriteFields(definition, packet); } for (int index = 0; index < m_Definition.Fields.Count; index++) { FieldDefinition fieldDefinition = m_Definition.Fields[index]; switch (fieldDefinition.FieldType) { case FieldType.Bool: packet.SetField(fieldDefinition.Name, (bool)m_FieldValues[index]); break; case FieldType.BoolArray: packet.SetField(fieldDefinition.Name, (bool[])m_FieldValues[index]); break; case FieldType.String: packet.SetField(fieldDefinition.Name, (string)m_FieldValues[index]); break; case FieldType.StringArray: packet.SetField(fieldDefinition.Name, (string[])m_FieldValues[index]); break; case FieldType.Int32: packet.SetField(fieldDefinition.Name, (Int32)m_FieldValues[index]); break; case FieldType.Int32Array: packet.SetField(fieldDefinition.Name, (Int32[])m_FieldValues[index]); break; case FieldType.Int64: packet.SetField(fieldDefinition.Name, (Int64)m_FieldValues[index]); break; case FieldType.Int64Array: packet.SetField(fieldDefinition.Name, (Int64[])m_FieldValues[index]); break; case FieldType.UInt32: packet.SetField(fieldDefinition.Name, (UInt32)m_FieldValues[index]); break; case FieldType.UInt32Array: packet.SetField(fieldDefinition.Name, (UInt32[])m_FieldValues[index]); break; case FieldType.UInt64: packet.SetField(fieldDefinition.Name, (UInt64)m_FieldValues[index]); break; case FieldType.UInt64Array: packet.SetField(fieldDefinition.Name, (UInt64[])m_FieldValues[index]); break; case FieldType.Double: packet.SetField(fieldDefinition.Name, (double)m_FieldValues[index]); break; case FieldType.DoubleArray: packet.SetField(fieldDefinition.Name, (double[])m_FieldValues[index]); break; case FieldType.TimeSpan: packet.SetField(fieldDefinition.Name, (TimeSpan)m_FieldValues[index]); break; case FieldType.TimeSpanArray: packet.SetField(fieldDefinition.Name, (TimeSpan[])m_FieldValues[index]); break; case FieldType.DateTime: packet.SetField(fieldDefinition.Name, (DateTime)m_FieldValues[index]); break; case FieldType.DateTimeArray: packet.SetField(fieldDefinition.Name, (DateTime[])m_FieldValues[index]); break; case FieldType.Guid: packet.SetField(fieldDefinition.Name, (Guid)m_FieldValues[index]); break; case FieldType.GuidArray: packet.SetField(fieldDefinition.Name, (Guid[])m_FieldValues[index]); break; case FieldType.DateTimeOffset: packet.SetField(fieldDefinition.Name, (DateTimeOffset)m_FieldValues[index]); break; case FieldType.DateTimeOffsetArray: packet.SetField(fieldDefinition.Name, (DateTimeOffset[])m_FieldValues[index]); break; default: #if DEBUG if (Debugger.IsAttached) { Debugger.Break(); } #endif throw new InvalidOperationException(string.Format("The field type {0} is unknown so we can't serialize the packet ", definition.Fields[index].FieldType)); } } }
/// <summary> /// Create a new serailized packet for deserialization /// </summary> /// <param name="definition"></param> /// <param name="values"></param> internal SerializedPacket(PacketDefinition definition, object[] values) { m_Definition = definition; m_Values = values; m_ReadOnly = true; }
public void Add(string fieldName, Type type) { Add(fieldName, PacketDefinition.GetSerializableType(type)); }
/// <summary> /// Create a new serialized packet for serialization /// </summary> /// <param name="definition"></param> internal SerializedPacket(PacketDefinition definition) { m_Definition = definition; m_Values = new object[definition.Fields.Count]; m_ReadOnly = false; }
/// <summary> /// Create a PacketDefinition describing the fields and serialization version information for the /// IPacket object passed by the caller. /// </summary> /// <param name="packet">IPacket object to generate a PacketDefinition for</param> /// <returns>PacketDefinition describing fields to be serialized, including nested types</returns> public static PacketDefinition CreatePacketDefinition(IPacket packet) { // These flags are used in the GetMethod call below. The key flag is DeclaredOnly // which is needed because we want to support object hierarchies that implement // IPacket at multiple levels. We accomplish this by called GetPacketDefinition // at each level in the hierarchy that implements IPacket. const BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic; // We iterate from the type we are passed down object hierarchy looking for // IPacket implementations. Then, on the way back up, we link together // the m_BasePacket fields to that the PacketDefinition we return includes // a description of all the nested types. Stack <PacketDefinition> stack = new Stack <PacketDefinition>(); Type type = packet.GetType(); // walk down the hierarchy till we get to a base object that no longer implements IPacket while (typeof(IPacket).IsAssignableFrom(type)) { // We push one PacketDefinition on the stack for each level in the hierarchy PacketDefinition definition; // Even though the current type implements IPacket, it may not have a GetPacketDefinition at this level MethodInfo method = GetIPacketMethod(type, "GetPacketDefinition", flags, Type.EmptyTypes); if (method != null) { definition = (PacketDefinition)method.Invoke(packet, new object[0]); definition.m_WriteMethod = GetIPacketMethod(type, "WriteFields", flags, new Type[] { typeof(PacketDefinition), typeof(SerializedPacket) }); if (definition.m_WriteMethod == null) { throw new GibraltarSerializationException("The current packet implements part but not all of the IPacket interface. No Write Method could be found. Did you implement IPacket explicitly?"); } if (definition.CanHaveRequiredPackets) { definition.m_GetRequiredPacketsMethod = GetIPacketMethod(type, "GetRequiredPackets", flags, Type.EmptyTypes); if (definition.m_GetRequiredPacketsMethod == null) { throw new GibraltarSerializationException("The current packet implements part but not all of the IPacket interface. No GetRequiredPackets Method could be found. Did you implement IPacket explicitly?"); } } } else { // If GetPacketDefinition isn't defined at this level, // push an empty PacketDefinition on the stack as a placeholder definition = new PacketDefinition(type.Name, -1, false); } // Push the PacketDefinition for this level on the stack // then iterate down to the next deeper level in the object hierarchy stack.Push(definition); type = type.GetTypeInfo().BaseType; } // At this point the top of the stack contains the mostly deeply nested base type. // While there are 2 or more elements on the stack, the deeper of the two // should reference the top element as a base type while (stack.Count >= 2) { // Pop off the deepest base type still in the stack PacketDefinition basePacket = stack.Pop(); // The next element is now visible, so let's peek at it PacketDefinition derivedPacket = stack.Peek(); // link the base type with its derived class derivedPacket.m_BasePacket = basePacket; } // At this point there should be exactly one element in the stack // which contains the return value for this method. PacketDefinition packetDefinition = stack.Pop(); // Check if this is a DynamicPacket. If so, it should have a unique dynamic type. // If the DynamicType field has not been assigned, assign a unique string. IDynamicPacket dynamicPacket = packet as IDynamicPacket; if (dynamicPacket != null) { if (dynamicPacket.DynamicTypeName == null) { dynamicPacket.DynamicTypeName = Guid.NewGuid().ToString(); } packetDefinition.m_DynamicTypeName = dynamicPacket.DynamicTypeName; } // Record whether or not this is a cachable packet. packetDefinition.m_IsCachable = packet is ICachedPacket; return(packetDefinition); }