/// <summary>
        /// Decodes an original stream and a delta stream, writing to a target stream.
        /// The original stream may be null, so long as the delta stream never
        /// refers to it. The original and delta streams must be readable, and the
        /// original stream (if any) and the target stream must be seekable.
        /// The target stream must be writable and readable. The original and target
        /// streams are rewound to their starts before any data is read; the relevant data
        /// must occur at the beginning of the original stream, and any data already present
        /// in the target stream may be overwritten. The delta data must begin
        /// wherever the delta stream is currently positioned. The delta stream must end
        /// after the last window. The streams are not disposed by this method.
        /// </summary>
        /// <param name="original">Stream containing delta. May be null.</param>
        /// <param name="delta">Stream containing delta data.</param>
        /// <param name="output">Stream to write resulting data to.</param>
        public static void Decode(Stream original, Stream delta, Stream output)
        {
            #region Simple argument checking
            if (original != null && (!original.CanRead || !original.CanSeek))
            {
                throw new ArgumentException("Must be able to read and seek in original stream", "original");
            }

            if (delta == null)
            {
                throw new ArgumentNullException(nameof(delta));
            }

            if (!delta.CanRead)
            {
                throw new ArgumentException("Unable to read from delta stream");
            }

            if (output == null)
            {
                throw new ArgumentNullException(nameof(output));
            }

            if (!output.CanWrite || !output.CanRead || !output.CanSeek)
            {
                throw new ArgumentException("Must be able to read, write and seek in output stream", "output");
            }

            #endregion

            // Now the arguments are checked, we construct an instance of the
            // class and ask it to do the decoding.
            VcdiffDecoder instance = new VcdiffDecoder(original, delta, output);
            instance.Decode();
        }
        /// <summary>
        /// Reads the custom code table, if there is one
        /// </summary>
        void ReadCodeTable()
        {
            // The length given includes the nearSize and sameSize bytes
            int compressedTableLength = IOHelper.ReadBigEndian7BitEncodedInt(delta) - 2;
            int nearSize = IOHelper.CheckedReadByte(delta);
            int sameSize = IOHelper.CheckedReadByte(delta);

            byte[] compressedTableData = IOHelper.CheckedReadBytes(delta, compressedTableLength);

            byte[] defaultTableData = CodeTable.Default.GetBytes();

            MemoryStream tableOriginal = new MemoryStream(defaultTableData, false);
            MemoryStream tableDelta    = new MemoryStream(compressedTableData, false);

            byte[]       decompressedTableData = new byte[1536];
            MemoryStream tableOutput           = new MemoryStream(decompressedTableData, true);

            VcdiffDecoder.Decode(tableOriginal, tableDelta, tableOutput);

            if (tableOutput.Position != 1536)
            {
                throw new VcdiffFormatException("Compressed code table was incorrect size");
            }

            _codeTable = new CodeTable(decompressedTableData);
            _cache     = new AddressCache(nearSize, sameSize);
        }