Пример #1
0
 public static void Create(Stream oldData, Stream newData, Stream output, ISuffixSort suffixSort = null)
 {
     using (var msold = new MemoryStream()) {
         using (var msnew = new MemoryStream()) {
             oldData.CopyTo(msold);
             newData.CopyTo(msnew);
             Create(msold.ToArray(), msnew.ToArray(), output, suffixSort);
         }
     }
 }
Пример #2
0
        private static void CreateInternal(byte[] oldData, byte[] newData, Stream output, ISuffixSort suffixSort)
        {
            // check arguments
            if (oldData == null)
            {
                throw new ArgumentNullException(nameof(oldData));
            }
            if (newData == null)
            {
                throw new ArgumentNullException(nameof(newData));
            }
            if (output == null)
            {
                throw new ArgumentNullException(nameof(output));
            }
            if (suffixSort == null)
            {
                throw new ArgumentNullException(nameof(suffixSort));
            }
            if (!output.CanSeek)
            {
                throw new ArgumentException("Output stream must be seekable.", nameof(output));
            }
            if (!output.CanWrite)
            {
                throw new ArgumentException("Output stream must be writable.", nameof(output));
            }

            /* Header is
             *  0	8	 "BSDIFF40"
             *  8	8	length of bzip2ed ctrl block
             *  16	8	length of bzip2ed diff block
             *  24	8	length of new file
             * File is
             *  0	32	Header
             *  32	??	Bzip2ed ctrl block
             *  ??	??	Bzip2ed diff block
             *  ??	??	Bzip2ed extra block */
            var header = new byte[HeaderSize];

            header.WriteLong(Signature);
            header.WriteLongAt(24, newData.Length);

            var startPosition = output.Position;

            output.Write(header, 0, header.Length);

            var I = suffixSort.Sort(oldData);

            using (var msControl = new MemoryStream())
                using (var msDiff = new MemoryStream())
                    using (var msExtra = new MemoryStream())
                    {
                        using (var ctrlStream = GetEncodingStream(msControl, true))
                            using (var diffStream = GetEncodingStream(msDiff, true))
                                using (var extraStream = GetEncodingStream(msExtra, true))
                                {
                                    var scan       = 0;
                                    var pos        = 0;
                                    var len        = 0;
                                    var lastscan   = 0;
                                    var lastpos    = 0;
                                    var lastoffset = 0;

                                    // compute the differences, writing ctrl as we go
                                    while (scan < newData.Length)
                                    {
                                        var oldscore = 0;

                                        for (var scsc = scan += len; scan < newData.Length; scan++)
                                        {
                                            len = Search(I, oldData, newData.Slice(scan), 0, oldData.Length, out pos);

                                            for (; scsc < scan + len; scsc++)
                                            {
                                                if ((scsc + lastoffset < oldData.Length) && (oldData[scsc + lastoffset] == newData[scsc]))
                                                {
                                                    oldscore++;
                                                }
                                            }

                                            if ((len == oldscore && len != 0) || (len > oldscore + 8))
                                            {
                                                break;
                                            }

                                            if ((scan + lastoffset < oldData.Length) && (oldData[scan + lastoffset] == newData[scan]))
                                            {
                                                oldscore--;
                                            }
                                        }

                                        if (len != oldscore || scan == newData.Length)
                                        {
                                            var s    = 0;
                                            var sf   = 0;
                                            var lenf = 0;
                                            for (var i = 0; (lastscan + i < scan) && (lastpos + i < oldData.Length);)
                                            {
                                                if (oldData[lastpos + i] == newData[lastscan + i])
                                                {
                                                    s++;
                                                }
                                                i++;
                                                if (s * 2 - i > sf * 2 - lenf)
                                                {
                                                    sf   = s;
                                                    lenf = i;
                                                }
                                            }

                                            var lenb = 0;
                                            if (scan < newData.Length)
                                            {
                                                s = 0;
                                                var sb = 0;
                                                for (var i = 1; (scan >= lastscan + i) && (pos >= i); i++)
                                                {
                                                    if (oldData[pos - i] == newData[scan - i])
                                                    {
                                                        s++;
                                                    }
                                                    if (s * 2 - i > sb * 2 - lenb)
                                                    {
                                                        sb   = s;
                                                        lenb = i;
                                                    }
                                                }
                                            }

                                            if (lastscan + lenf > scan - lenb)
                                            {
                                                var overlap = (lastscan + lenf) - (scan - lenb);
                                                s = 0;
                                                var ss   = 0;
                                                var lens = 0;
                                                for (var i = 0; i < overlap; i++)
                                                {
                                                    if (newData[lastscan + lenf - overlap + i] == oldData[lastpos + lenf - overlap + i])
                                                    {
                                                        s++;
                                                    }
                                                    if (newData[scan - lenb + i] == oldData[pos - lenb + i])
                                                    {
                                                        s--;
                                                    }
                                                    if (s > ss)
                                                    {
                                                        ss   = s;
                                                        lens = i + 1;
                                                    }
                                                }

                                                lenf += lens - overlap;
                                                lenb -= lens;
                                            }

                                            //write diff string
                                            for (var i = 0; i < lenf; i++)
                                            {
                                                diffStream.WriteByte((byte)(newData[lastscan + i] - oldData[lastpos + i]));
                                            }

                                            //write extra string
                                            var extraLength = (scan - lenb) - (lastscan + lenf);
                                            if (extraLength > 0)
                                            {
                                                extraStream.Write(newData, lastscan + lenf, extraLength);
                                            }

                                            //backing for ctrl writes
                                            var buf = new byte[8];

                                            //write ctrl block
                                            buf.WriteLong(lenf);
                                            ctrlStream.Write(buf, 0, 8);

                                            buf.WriteLong(extraLength);
                                            ctrlStream.Write(buf, 0, 8);

                                            buf.WriteLong((pos - lenb) - (lastpos + lenf));
                                            ctrlStream.Write(buf, 0, 8);

                                            lastscan   = scan - lenb;
                                            lastpos    = pos - lenb;
                                            lastoffset = pos - scan;
                                        }
                                    }
                                }

                        //write compressed ctrl data
                        msControl.Seek(0, SeekOrigin.Begin);
                        msControl.CopyTo(output);

                        // compute size of compressed ctrl data
                        header.WriteLongAt(8, msControl.Length);

                        // write compressed diff data
                        msDiff.Seek(0, SeekOrigin.Begin);
                        msDiff.CopyTo(output);

                        // compute size of compressed diff data
                        header.WriteLongAt(16, msDiff.Length);

                        // write compressed extra data
                        msExtra.Seek(0, SeekOrigin.Begin);
                        msExtra.CopyTo(output);
                    }

            // seek to the beginning, write the header, then seek back to end
            var endPosition = output.Position;

            output.Position = startPosition;
            output.Write(header, 0, header.Length);
            output.Position = endPosition;
        }
Пример #3
0
 /// <summary>
 /// Creates a BSDIFF-format patch from two byte arrays
 /// </summary>
 /// <param name="oldData">Byte array of the original (older) data</param>
 /// <param name="newData">Byte array of the changed (newer) data</param>
 /// <param name="output">Seekable, writable stream where the patch will be written</param>
 /// <param name="suffixSort">Suffix sort implementation to use for comparison, or null to use a default sorter</param>
 public static void Create(byte[] oldData, byte[] newData, Stream output, ISuffixSort suffixSort = null)
 {
     CreateInternal(oldData, newData, output, suffixSort ?? DefaultSuffixSort.Value);
 }