Example #1
0
        /// <summary>
        ///     Stores meta data (i.e. data needed to decompress and test the file) for the compressed <paramref name="filePart" />
        ///     .
        /// </summary>
        /// <param name="filePart">The compressed file which meta data should be stored.</param>
        /// <param name="frequencyTable">The frequency table needed to restore the Huffman tree (for file decoding). </param>
        /// <param name="uncompressedHashSum">The hashsum needed to test file correctness after unpacking.</param>
        /// <param name="compressedHashSum">The hashsum needed to test compressed file correctness before unpacking.</param>
        /// <param name="ratio">The compression ratio shown to the user.</param>
        /// <param name="uncompressed">Size of the file before compression.</param>
        /// <param name="compressed">Size of the compressed file.</param>
        private void StoreMetaData(PackagePart filePart, FrequencyTable frequencyTable, byte[] uncompressedHashSum,
                                   byte[] compressedHashSum, double ratio, long uncompressed, long compressed, string name)
        {
            /* collect metadate into a class */
            var meta = new FileMetaData
            {
                frequencyTable   = frequencyTable,
                compressedHash   = compressedHashSum,
                uncompressedHash = uncompressedHashSum,
                compressionRatio = ratio,
                uncompressedSize = uncompressed,
                compressedSize   = compressed,
                filename         = name
            };

            /* create path (relating to the archive root) where the meta data will be stored */
            var metaUri = PackUriHelper.CreatePartUri(new Uri(filePart.Uri + "META", UriKind.Relative));
            /* create a package part in the path */
            var metaPart = _archive.CreatePart(metaUri, "", CompressionOption.NotCompressed);

            filePart.CreateRelationship(metaUri, TargetMode.Internal, ".\\META");

            using (var metaStream = new BufferedStream(metaPart.GetStream(), BufferSize))
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(metaStream, meta);
            }
        }
Example #2
0
        /// <summary>
        ///     Compresses the file stored in the <paramref name="path" /> and stores compressed file it the
        ///     <paramref name="filePart" />.
        /// </summary>
        /// <param name="path">The path to the file to be compressed.</param>
        /// <param name="filePart">The archive part to store the compressed file in.</param>
        /// <param name="frequencyTable">The frequency table to be saved to the archive.</param>
        private void CompressFile(string path, PackagePart filePart, out FrequencyTable frequencyTable)
        {
            /* open the file */
            var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize);

            /* find frequencies of bytes in the file */
            OnStateChange("counting up frequencies of bytes");
            frequencyTable = new FrequencyTable(fileStream);
            frequencyTable.SubscribeToUpdates(OnProgressChange);
            frequencyTable.CountFrequencies();
            /* rewind the position of the stream to use this stream once again */
            fileStream.Position = 0;
            /* build the Huffman tree based on the counted frequencies */
            OnStateChange("building the Huffman tree");
            var huffmanTree = new HuffmanTree(frequencyTable);

            /* get the Huffman code from the tree */
            OnStateChange("retrieving the Huffman code");
            var encodingTable = new EncodingTable(huffmanTree);
            /* get the file part's stream */
            var filePartStream = new BufferedStream(filePart.GetStream(), BufferSize);

            /* encode the file */
            OnStateChange("compressing the file");
            var encoder = new Encoder(fileStream, filePartStream, encodingTable);

            encoder.SubscribeToUpdates(OnProgressChange);
            encoder.Encode();
            /* dispose unused streams */
            fileStream.Close();
            filePartStream.Close();
        }
Example #3
0
 /// <summary>
 ///     Initializes a new instance of the decoder.
 /// </summary>
 /// <param name="frequencyTable">The frequency table to build the Huffman tree from.</param>
 /// <param name="encoodedBytesCount">The number of bytes that should be decoded and written.</param>
 public Decoder(Stream input, Stream output, FrequencyTable frequencyTable, long encoodedBytesCount)
 {
     InputStream  = input;
     OutputStream = output;
     Tree         = new HuffmanTree(frequencyTable);
     BytesCount   = encoodedBytesCount;
 }
Example #4
0
        /// <summary>
        ///     Packs non-zero frequency bytes to Huffman tree leaves and joins it to corresponding bytes' frequencies.
        /// </summary>
        /// <param name="freq">The frequency table to get nodes from.</param>
        /// <returns>Weighted nodes containing Huffman tree leaves and their weights.</returns>
        private static WeightedNode[] PackNodes(FrequencyTable freq)
        {
            var weightedNodes = new List <WeightedNode>();

            for (int @byte = byte.MinValue; @byte <= byte.MaxValue; @byte++)
            {
                /* for each byte in the frequency table */
                if (freq[@byte] != 0)
                {
                    /* if the byte occurs in the stream at least once */
                    weightedNodes.Add(new WeightedNode {
                        node = new Leaf(@byte), weight = freq[@byte]
                    });
                }
            }

            return(weightedNodes.ToArray());
        }
Example #5
0
        public static Node GenerateHuffmanTree(FrequencyTable frequencyTable, out int height)
        {
            //TODO enhance this code (assuming that both arrays have the same lenght)
            /* check frequency table */
            if (frequencyTable == null)
            {
                throw new ArgumentNullException(nameof(frequencyTable), "The provided frequency table is null.");
            }

            /* set initial height */
            height = 0;

            /* the array contaning leaves of a tree */
            var leaves = PackNodes(frequencyTable);

            Array.Sort(leaves);
            if (leaves.Length == 1)
            {
                return(leaves[0].node);

                height = 1;
            }

            if (leaves.Length == 0)
            {
                return(null);
            }

            /* the array containing internal nodes of a tree */
            var
                internalNodes =
                new WeightedNode[leaves.Length -
                                 1];     // (because Huffman tree is a full binary tree the array containg leaves-1 elements)

            /* fill internailNodes with null values (null means node of infinite weight) */
            FillWithNull(ref internalNodes);

            /* the index of the current element of the leaves array */
            var leavesIndex = 0;
            /* the index of the current element of the internalNodes array */
            var internalNodesIndex = 0;

            var internalNodesEnd = 0;

            /* current sum value */
            long currentSum;

            /* current (withing a loop iteration) min sum value */
            long minSum;

            /* special marker variable for determining the exact two elements of the min sum */
            var status = "";


            for (var i = 0; i <= internalNodes.Length - 1; i++)
            /* while  */
            {
                height += 1;
                minSum  = long.MaxValue;

                if (leavesIndex + 1 < leaves.Length)
                /* if two sequential elements (relating to leavesIndex) of leaves array exist */
                {
                    /* check if their sum is less than the min sum (and set it as the new min sum if so) */
                    currentSum = leaves[leavesIndex].weight + leaves[leavesIndex + 1].weight;
                    if (currentSum < minSum)
                    {
                        minSum = currentSum;
                        status = "leaves + leaves";
                    }
                }

                if (leavesIndex < leaves.Length && internalNodes[internalNodesIndex] != null)
                /* if current internal node's weight is not equal to infinity */
                {
                    /* check if sum of current leaf and internal node is less than the min sum (and set it as the new min sum if so) */
                    currentSum = leaves[leavesIndex].weight + internalNodes[internalNodesIndex].weight;
                    if (currentSum < minSum)
                    {
                        minSum = currentSum;
                        status = "leaves + internalNodes";
                    }
                }

                if (internalNodes[internalNodesIndex] != null && internalNodesIndex + 1 < internalNodes.Length)
                {
                    /* if two sequential elements (relating to internalNodesIndex) of internalNodes array exist */
                    if (internalNodes[internalNodesIndex + 1] != null)
                    {
                        /* check if sum of current leaf and internal node is less than the min sum (and set it as the new min sum if so) */
                        currentSum = internalNodes[internalNodesIndex].weight +
                                     internalNodes[internalNodesIndex + 1].weight;
                        if (currentSum < minSum)
                        {
                            minSum = currentSum;
                            status = "internalNodes + internalNodes";
                        }
                    }
                }


                WeightedNode w1, w2;

                switch (status)
                {
                case "leaves + leaves":
                    w1 = leaves[leavesIndex];
                    w2 = leaves[leavesIndex + 1];
                    internalNodes[internalNodesEnd++] = new WeightedNode
                    {
                        node   = new InternalNode(w1.node, w2.node),
                        weight = w1.weight + w2.weight
                    };
                    leavesIndex += 2;
                    break;

                case "leaves + internalNodes":
                    w1 = leaves[leavesIndex];
                    w2 = internalNodes[internalNodesIndex];
                    internalNodes[internalNodesEnd++] = new WeightedNode
                    {
                        node   = new InternalNode(w1.node, w2.node),
                        weight = w1.weight + w2.weight
                    };
                    leavesIndex        += 1;
                    internalNodesIndex += 1;
                    break;

                case "internalNodes + internalNodes":
                    w1 = internalNodes[internalNodesIndex];
                    w2 = internalNodes[internalNodesIndex + 1];
                    internalNodes[internalNodesEnd++] = new WeightedNode
                    {
                        node   = new InternalNode(w1.node, w2.node),
                        weight = w1.weight + w2.weight
                    };
                    internalNodesIndex += 2;
                    break;

                default:
                    throw new NotImplementedException();
                }
            }

            return(internalNodes[internalNodes.Length - 1].node);
        }