public CompressedMessagePackage Encode(byte[] message) { NoteTemplateUse(); CompressedMessagePackage cmp = new CompressedMessagePackage(); cmp.TemplateId = templateId; // Encode with the trie compressor -- compress repeated sequences byte[] encoded = tc.Encode(message); // FIXME: don't currently understand the skip compressor anyways //encoded = ct.SkipCompressor.Compress(encoded); //sc.AddPattern(encoded); // If enabled, and it improves the case, use huffman encoding to replace // frequently-used bytes byte[] huffed = hc.Encode(encoded); if (huffed != null && huffed.Length < encoded.Length) { cmp.Huffed = true; cmp.Message = huffed; // Only send the huffman frequency table when actually necessary! // After all, we might have a few updates before it's actually received... cmp.FrequencyTable = frequencies; frequenciesSent = true; // pending updates are scheduled to be sent } else { cmp.Huffed = false; cmp.Message = encoded; // we must reset this flag. Although we may have been scheduled to send the pending // frequency updates on a previous call, the update may not have been sent if // another template was selected. frequenciesSent = false; } if (cmp.Message.Length < message.Length) { encodedSavings += message.Length - cmp.Message.Length; } cmp.Gmced = true; // FIXME: could check that encoded.Length < message.Length if (!templateSent) { cmp.Template = template; } cmp.Announcements = dictionaryAdditions; return cmp; }
/// <summary> /// Decode the provided byte array received from the user with unique /// identity <c>userId</c>. /// </summary> /// <param name="userId">the unique identity for the user</param> /// <param name="encodedBytes">the encoded message received</param> /// <returns></returns> public byte[] Decode(int userId, byte[] encodedBytes) { log.Trace("\n>>>> DECODING <<<<<"); Stream input = TryInflating(encodedBytes); CompressedMessagePackage cmp = new CompressedMessagePackage(); cmp.TemplateId = (short)ByteUtils.DecodeLength(input); log.Trace(String.Format("==> Decoding with template {0}", cmp.TemplateId)); int inputValue; while((inputValue = input.ReadByte()) != -1) { switch ((GMCMessageKey)inputValue) { case GMCMessageKey.Huffed: cmp.Huffed = true; break; case GMCMessageKey.NotHuffed: cmp.Huffed = false; break; case GMCMessageKey.Template: cmp.Template = DecodeTemplate(input); if (log.IsTraceEnabled) { log.Trace(String.Format("TID={0}: Decoded template: {1}", cmp.TemplateId, ByteUtils.DumpBytes(cmp.Template))); } break; case GMCMessageKey.HuffmanFrequencyTable: cmp.FrequencyTable = DecodeFrequencies(input); log.Trace(String.Format("TID={0}: Decoded huffman frequencies", cmp.TemplateId)); break; case GMCMessageKey.Announcements: cmp.Announcements = DecodeAnnouncements(input); if (log.IsTraceEnabled) { StringBuilder message = new StringBuilder(); message.Append(String.Format("TID={0}: Decoded {1} dictionary announcements:", cmp.TemplateId, cmp.Announcements.Count)); foreach(KeyValuePair<uint, byte> kvp in cmp.Announcements) { message.Append(' '); message.Append(kvp.Key); message.Append("->"); message.Append(kvp.Value); } log.Trace(message); } break; case GMCMessageKey.Message: uint len = ByteUtils.DecodeLength(input); cmp.Message = new byte[len]; input.Read(cmp.Message, 0, (int)len); if (log.IsTraceEnabled) { log.Trace(String.Format("Decoded message: {0}", ByteUtils.DumpBytes(cmp.Message))); } break; default: log.Trace("Invalid GMC message"); throw new MarshallingException("invalid GMC message"); } } GeneralMessageCompressor handler; if (!decompressors.TryGetValue(userId, out handler)) { if (cmp.Template == null) { //we haven't received any templates whatsoever from this person. MissingInformationException mte = new MissingInformationException(); mte.Template = 0; mte.UserID = userId; mte.ExceptionType = EnumExceptionType.MissingTemplate; throw mte; } handler = decompressors[userId] = new GeneralMessageCompressor(); } return handler.Decode(cmp, userId); }
/// <summary> /// Decompresses a message using the provided compressors. /// </summary> /// <param name="cmp">the details of the message to be decompressed</param> /// <returns>The decoded (uncompressed) message</returns> public byte[] Decode(CompressedMessagePackage cmp) { List<byte> missingAnnouncements = new List<byte>(); if (cmp.FrequencyTable != null) { Debug.Assert(cmp.FrequencyTable.Length == 256); hc.SetFrequencies(cmp.FrequencyTable); if(log.IsTraceEnabled) { log.Trace(String.Format("[tid={0}] received huffman frequencies {1}", templateId, cmp.FrequencyTable)); } } if (cmp.Announcements != null) { foreach (uint longForm in cmp.Announcements.Keys) { tc.HandleAnnouncement(longForm, cmp.Announcements[longForm]); } } byte[] message = cmp.Message; Debug.Assert(message != null); if (cmp.Huffed) { message = hc.Decode(message); } Debug.Assert(message != null); // if(cmp.Gmced) { message = tc.Decode(message); // } return message; }
/// <summary> /// Decode the provided message using the specified template /// </summary> /// <param name="cmp">the message package to be decompressed</param> /// <param name="userId">the user from which the encoded message was received</param> /// <returns>The decoded (uncompressed) message</returns> public byte[] Decode(CompressedMessagePackage cmp, int userId) { if (cmp.Template != null) { AddTemplate(cmp.Template, cmp.TemplateId); } if (cmp.TemplateId < 0 || cmp.TemplateId > compressors.Count) { MissingInformationException mte = new MissingInformationException(); mte.Template = cmp.TemplateId; mte.UserID = userId; mte.ExceptionType = EnumExceptionType.MissingTemplate; throw mte; } return compressors[cmp.TemplateId].Decode(cmp); }