/// <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 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(); }