wabbajack/Wabbajack.Common/BSDiff.cs

680 lines
26 KiB
C#
Raw Normal View History

2019-09-14 04:35:42 +00:00
using System;
using System.IO;
using ICSharpCode.SharpZipLib.BZip2;
namespace Wabbajack.Common
2019-07-21 22:47:17 +00:00
{
/*
The original bsdiff.c source code (http://www.daemonology.net/bsdiff/) is
distributed under the following license:
Copyright 2003-2005 Colin Percival
All rights reserved
Redistribution and use in source and binary forms, with or without
modification, are permitted providing that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
2019-07-26 20:59:14 +00:00
/*
* Includes fixes from https://github.com/mendsley/bsdiff/pull/12/files
* translated from C to C# to prevent stack overflows.
*/
2019-07-21 22:47:17 +00:00
public class BSDiff
{
2019-09-14 04:35:42 +00:00
private const long c_fileSignature = 0x3034464649445342L;
private const int c_headerSize = 32;
2019-07-21 22:47:17 +00:00
/// <summary>
2019-09-14 04:35:42 +00:00
/// Creates a binary patch (in <a href="http://www.daemonology.net/bsdiff/">bsdiff</a> format) that can be used
/// (by <see cref="Apply" />) to transform <paramref name="oldData" /> into <paramref name="newData" />.
2019-07-21 22:47:17 +00:00
/// </summary>
/// <param name="oldData">The original binary data.</param>
/// <param name="newData">The new binary data.</param>
2019-09-14 04:35:42 +00:00
/// <param name="output">A <see cref="Stream" /> to which the patch will be written.</param>
2019-07-21 22:47:17 +00:00
public static void Create(byte[] oldData, byte[] newData, Stream output)
{
// check arguments
if (oldData == null)
throw new ArgumentNullException("oldData");
if (newData == null)
throw new ArgumentNullException("newData");
if (output == null)
throw new ArgumentNullException("output");
if (!output.CanSeek)
throw new ArgumentException("Output stream must be seekable.", "output");
if (!output.CanWrite)
throw new ArgumentException("Output stream must be writable.", "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 */
2019-09-14 04:35:42 +00:00
var header = new byte[c_headerSize];
2019-07-21 22:47:17 +00:00
WriteInt64(c_fileSignature, header, 0); // "BSDIFF40"
WriteInt64(0, header, 8);
WriteInt64(0, header, 16);
WriteInt64(newData.Length, header, 24);
2019-09-14 04:35:42 +00:00
var startPosition = output.Position;
2019-07-21 22:47:17 +00:00
output.Write(header, 0, header.Length);
2019-09-14 04:35:42 +00:00
var I = SuffixSort(oldData);
2019-07-21 22:47:17 +00:00
2019-09-14 04:35:42 +00:00
var db = new byte[newData.Length];
var eb = new byte[newData.Length];
2019-07-21 22:47:17 +00:00
2019-09-14 04:35:42 +00:00
var dblen = 0;
var eblen = 0;
2019-07-21 22:47:17 +00:00
2019-09-14 04:35:42 +00:00
using (var bz2Stream = new BZip2OutputStream(output) {IsStreamOwner = false})
2019-07-21 22:47:17 +00:00
{
// compute the differences, writing ctrl as we go
2019-09-14 04:35:42 +00:00
var scan = 0;
var pos = 0;
var len = 0;
var lastscan = 0;
var lastpos = 0;
var lastoffset = 0;
2019-07-21 22:47:17 +00:00
while (scan < newData.Length)
{
2019-09-14 04:35:42 +00:00
var oldscore = 0;
2019-07-21 22:47:17 +00:00
2019-09-14 04:35:42 +00:00
for (var scsc = scan += len; scan < newData.Length; scan++)
2019-07-21 22:47:17 +00:00
{
len = Search(I, oldData, newData, scan, 0, oldData.Length, out pos);
for (; scsc < scan + len; scsc++)
2019-09-14 04:35:42 +00:00
if (scsc + lastoffset < oldData.Length && oldData[scsc + lastoffset] == newData[scsc])
2019-07-21 22:47:17 +00:00
oldscore++;
2019-09-14 04:35:42 +00:00
if (len == oldscore && len != 0 || len > oldscore + 8)
2019-07-21 22:47:17 +00:00
break;
2019-09-14 04:35:42 +00:00
if (scan + lastoffset < oldData.Length && oldData[scan + lastoffset] == newData[scan])
2019-07-21 22:47:17 +00:00
oldscore--;
}
if (len != oldscore || scan == newData.Length)
{
2019-09-14 04:35:42 +00:00
var s = 0;
var sf = 0;
var lenf = 0;
for (var i = 0; lastscan + i < scan && lastpos + i < oldData.Length;)
2019-07-21 22:47:17 +00:00
{
if (oldData[lastpos + i] == newData[lastscan + i])
s++;
i++;
if (s * 2 - i > sf * 2 - lenf)
{
sf = s;
lenf = i;
}
}
2019-09-14 04:35:42 +00:00
var lenb = 0;
2019-07-21 22:47:17 +00:00
if (scan < newData.Length)
{
s = 0;
2019-09-14 04:35:42 +00:00
var sb = 0;
for (var i = 1; scan >= lastscan + i && pos >= i; i++)
2019-07-21 22:47:17 +00:00
{
if (oldData[pos - i] == newData[scan - i])
s++;
if (s * 2 - i > sb * 2 - lenb)
{
sb = s;
lenb = i;
}
}
}
if (lastscan + lenf > scan - lenb)
{
2019-09-14 04:35:42 +00:00
var overlap = lastscan + lenf - (scan - lenb);
2019-07-21 22:47:17 +00:00
s = 0;
2019-09-14 04:35:42 +00:00
var ss = 0;
var lens = 0;
for (var i = 0; i < overlap; i++)
2019-07-21 22:47:17 +00:00
{
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;
}
2019-09-14 04:35:42 +00:00
for (var i = 0; i < lenf; i++)
db[dblen + i] = (byte) (newData[lastscan + i] - oldData[lastpos + i]);
for (var i = 0; i < scan - lenb - (lastscan + lenf); i++)
2019-07-21 22:47:17 +00:00
eb[eblen + i] = newData[lastscan + lenf + i];
dblen += lenf;
2019-09-14 04:35:42 +00:00
eblen += scan - lenb - (lastscan + lenf);
2019-07-21 22:47:17 +00:00
2019-09-14 04:35:42 +00:00
var buf = new byte[8];
2019-07-21 22:47:17 +00:00
WriteInt64(lenf, buf, 0);
bz2Stream.Write(buf, 0, 8);
2019-09-14 04:35:42 +00:00
WriteInt64(scan - lenb - (lastscan + lenf), buf, 0);
2019-07-21 22:47:17 +00:00
bz2Stream.Write(buf, 0, 8);
2019-09-14 04:35:42 +00:00
WriteInt64(pos - lenb - (lastpos + lenf), buf, 0);
2019-07-21 22:47:17 +00:00
bz2Stream.Write(buf, 0, 8);
lastscan = scan - lenb;
lastpos = pos - lenb;
lastoffset = pos - scan;
}
}
}
// compute size of compressed ctrl data
2019-09-14 04:35:42 +00:00
var controlEndPosition = output.Position;
2019-07-21 22:47:17 +00:00
WriteInt64(controlEndPosition - startPosition - c_headerSize, header, 8);
// write compressed diff data
2019-09-14 04:35:42 +00:00
using (var bz2Stream = new BZip2OutputStream(output) {IsStreamOwner = false})
2019-07-21 22:47:17 +00:00
{
bz2Stream.Write(db, 0, dblen);
}
// compute size of compressed diff data
2019-09-14 04:35:42 +00:00
var diffEndPosition = output.Position;
2019-07-21 22:47:17 +00:00
WriteInt64(diffEndPosition - controlEndPosition, header, 16);
// write compressed extra data
2019-09-14 04:35:42 +00:00
using (var bz2Stream = new BZip2OutputStream(output) {IsStreamOwner = false})
2019-07-21 22:47:17 +00:00
{
bz2Stream.Write(eb, 0, eblen);
}
// seek to the beginning, write the header, then seek back to end
2019-09-14 04:35:42 +00:00
var endPosition = output.Position;
2019-07-21 22:47:17 +00:00
output.Position = startPosition;
output.Write(header, 0, header.Length);
output.Position = endPosition;
}
/// <summary>
2019-09-14 04:35:42 +00:00
/// Applies a binary patch (in <a href="http://www.daemonology.net/bsdiff/">bsdiff</a> format) to the data in
/// <paramref name="input" /> and writes the results of patching to <paramref name="output" />.
2019-07-21 22:47:17 +00:00
/// </summary>
2019-09-14 04:35:42 +00:00
/// <param name="input">A <see cref="Stream" /> containing the input data.</param>
/// <param name="openPatchStream">
/// A func that can open a <see cref="Stream" /> positioned at the start of the patch data.
/// This stream must support reading and seeking, and <paramref name="openPatchStream" /> must allow multiple streams
/// on
/// the patch to be opened concurrently.
/// </param>
/// <param name="output">A <see cref="Stream" /> to which the patched data is written.</param>
2019-07-21 22:47:17 +00:00
public static void Apply(Stream input, Func<Stream> openPatchStream, Stream output)
{
// check arguments
if (input == null)
throw new ArgumentNullException("input");
if (openPatchStream == null)
throw new ArgumentNullException("openPatchStream");
if (output == null)
throw new ArgumentNullException("output");
/*
File format:
0 8 "BSDIFF40"
8 8 X
16 8 Y
24 8 sizeof(newfile)
32 X bzip2(control block)
32+X Y bzip2(diff block)
32+X+Y ??? bzip2(extra block)
with control block a set of triples (x,y,z) meaning "add x bytes
from oldfile to x bytes from the diff block; copy y bytes from the
extra block; seek forwards in oldfile by z bytes".
*/
// read header
long controlLength, diffLength, newSize;
2019-09-14 04:35:42 +00:00
using (var patchStream = openPatchStream())
2019-07-21 22:47:17 +00:00
{
// check patch stream capabilities
if (!patchStream.CanRead)
throw new ArgumentException("Patch stream must be readable.", "openPatchStream");
if (!patchStream.CanSeek)
throw new ArgumentException("Patch stream must be seekable.", "openPatchStream");
2019-09-14 04:35:42 +00:00
var header = ReadExactly(patchStream, c_headerSize);
2019-07-21 22:47:17 +00:00
// check for appropriate magic
2019-09-14 04:35:42 +00:00
var signature = ReadInt64(header, 0);
2019-07-21 22:47:17 +00:00
if (signature != c_fileSignature)
throw new InvalidOperationException("Corrupt patch.");
// read lengths from header
controlLength = ReadInt64(header, 8);
diffLength = ReadInt64(header, 16);
newSize = ReadInt64(header, 24);
if (controlLength < 0 || diffLength < 0 || newSize < 0)
throw new InvalidOperationException("Corrupt patch.");
}
// preallocate buffers for reading and writing
const int c_bufferSize = 1048576;
2019-09-14 04:35:42 +00:00
var newData = new byte[c_bufferSize];
var oldData = new byte[c_bufferSize];
2019-07-21 22:47:17 +00:00
// prepare to read three parts of the patch in parallel
2019-09-14 04:35:42 +00:00
using (var compressedControlStream = openPatchStream())
using (var compressedDiffStream = openPatchStream())
using (var compressedExtraStream = openPatchStream())
2019-07-21 22:47:17 +00:00
{
// seek to the start of each part
compressedControlStream.Seek(c_headerSize, SeekOrigin.Current);
compressedDiffStream.Seek(c_headerSize + controlLength, SeekOrigin.Current);
compressedExtraStream.Seek(c_headerSize + controlLength + diffLength, SeekOrigin.Current);
// decompress each part (to read it)
2019-09-14 04:35:42 +00:00
using (var controlStream = new BZip2InputStream(compressedControlStream))
using (var diffStream = new BZip2InputStream(compressedDiffStream))
using (var extraStream = new BZip2InputStream(compressedExtraStream))
2019-07-21 22:47:17 +00:00
{
2019-09-14 04:35:42 +00:00
var control = new long[3];
var buffer = new byte[8];
2019-07-21 22:47:17 +00:00
2019-09-14 04:35:42 +00:00
var oldPosition = 0;
var newPosition = 0;
2019-07-21 22:47:17 +00:00
while (newPosition < newSize)
{
// read control data
2019-09-14 04:35:42 +00:00
for (var i = 0; i < 3; i++)
2019-07-21 22:47:17 +00:00
{
ReadExactly(controlStream, buffer, 0, 8);
control[i] = ReadInt64(buffer, 0);
}
// sanity-check
if (newPosition + control[0] > newSize)
throw new InvalidOperationException("Corrupt patch.");
// seek old file to the position that the new data is diffed against
input.Position = oldPosition;
2019-09-14 04:35:42 +00:00
var bytesToCopy = (int) control[0];
2019-07-21 22:47:17 +00:00
while (bytesToCopy > 0)
{
2019-09-14 04:35:42 +00:00
var actualBytesToCopy = Math.Min(bytesToCopy, c_bufferSize);
2019-07-21 22:47:17 +00:00
// read diff string
ReadExactly(diffStream, newData, 0, actualBytesToCopy);
// add old data to diff string
2019-09-14 04:35:42 +00:00
var availableInputBytes =
Math.Min(actualBytesToCopy, (int) (input.Length - input.Position));
2019-07-21 22:47:17 +00:00
ReadExactly(input, oldData, 0, availableInputBytes);
2019-09-14 04:35:42 +00:00
for (var index = 0; index < availableInputBytes; index++)
2019-07-21 22:47:17 +00:00
newData[index] += oldData[index];
output.Write(newData, 0, actualBytesToCopy);
// adjust counters
newPosition += actualBytesToCopy;
oldPosition += actualBytesToCopy;
bytesToCopy -= actualBytesToCopy;
}
// sanity-check
if (newPosition + control[1] > newSize)
throw new InvalidOperationException("Corrupt patch.");
// read extra string
2019-09-14 04:35:42 +00:00
bytesToCopy = (int) control[1];
2019-07-21 22:47:17 +00:00
while (bytesToCopy > 0)
{
2019-09-14 04:35:42 +00:00
var actualBytesToCopy = Math.Min(bytesToCopy, c_bufferSize);
2019-07-21 22:47:17 +00:00
ReadExactly(extraStream, newData, 0, actualBytesToCopy);
output.Write(newData, 0, actualBytesToCopy);
newPosition += actualBytesToCopy;
bytesToCopy -= actualBytesToCopy;
}
// adjust position
2019-09-14 04:35:42 +00:00
oldPosition = (int) (oldPosition + control[2]);
2019-07-21 22:47:17 +00:00
}
}
}
}
private static int CompareBytes(byte[] left, int leftOffset, byte[] right, int rightOffset)
{
2019-09-14 04:35:42 +00:00
for (var index = 0; index < left.Length - leftOffset && index < right.Length - rightOffset; index++)
2019-07-21 22:47:17 +00:00
{
2019-09-14 04:35:42 +00:00
var diff = left[index + leftOffset] - right[index + rightOffset];
2019-07-21 22:47:17 +00:00
if (diff != 0)
return diff;
}
2019-09-14 04:35:42 +00:00
2019-07-21 22:47:17 +00:00
return 0;
}
private static int MatchLength(byte[] oldData, int oldOffset, byte[] newData, int newOffset)
{
int i;
for (i = 0; i < oldData.Length - oldOffset && i < newData.Length - newOffset; i++)
if (oldData[i + oldOffset] != newData[i + newOffset])
break;
return i;
}
2019-09-14 04:35:42 +00:00
private static int Search(int[] I, byte[] oldData, byte[] newData, int newOffset, int start, int end,
out int pos)
2019-07-21 22:47:17 +00:00
{
if (end - start < 2)
{
2019-09-14 04:35:42 +00:00
var startLength = MatchLength(oldData, I[start], newData, newOffset);
var endLength = MatchLength(oldData, I[end], newData, newOffset);
2019-07-21 22:47:17 +00:00
if (startLength > endLength)
{
pos = I[start];
return startLength;
}
2019-09-14 04:35:42 +00:00
pos = I[end];
return endLength;
2019-07-21 22:47:17 +00:00
}
2019-09-14 04:35:42 +00:00
var midPoint = start + (end - start) / 2;
return CompareBytes(oldData, I[midPoint], newData, newOffset) < 0
? Search(I, oldData, newData, newOffset, midPoint, end, out pos)
: Search(I, oldData, newData, newOffset, start, midPoint, out pos);
2019-07-21 22:47:17 +00:00
}
private static void Split(int[] I, int[] v, int start, int len, int h)
{
if (len < 16)
{
int j;
2019-09-14 04:35:42 +00:00
for (var k = start; k < start + len; k += j)
2019-07-21 22:47:17 +00:00
{
j = 1;
2019-09-14 04:35:42 +00:00
var x = v[I[k] + h];
for (var i = 1; k + i < start + len; i++)
2019-07-21 22:47:17 +00:00
{
if (v[I[k + i] + h] < x)
{
x = v[I[k + i] + h];
j = 0;
}
2019-09-14 04:35:42 +00:00
2019-07-21 22:47:17 +00:00
if (v[I[k + i] + h] == x)
{
Swap(ref I[k + j], ref I[k + i]);
j++;
}
}
2019-09-14 04:35:42 +00:00
for (var i = 0; i < j; i++)
2019-07-21 22:47:17 +00:00
v[I[k + i]] = k + j - 1;
if (j == 1)
I[k] = -1;
}
}
else
{
2019-07-26 20:59:14 +00:00
int y, z, j, k, x, tmp;
j = start + len / 2;
k = start + len - 1;
x = v[I[j] + h];
y = v[I[start] + h];
z = v[I[k] + h];
if (len > 40)
2019-09-14 04:35:42 +00:00
{
/* Big array: Pseudomedian of 9 */
2019-07-26 20:59:14 +00:00
tmp = len / 8;
x = median3(x, v[I[j - tmp] + h], v[I[j + tmp] + h]);
y = median3(y, v[I[start + tmp] + h], v[I[start + tmp + tmp] + h]);
z = median3(z, v[I[k - tmp] + h], v[I[k - tmp - tmp] + h]);
2019-09-14 04:35:42 +00:00
}
; /* Else medium array: Pseudomedian of 3 */
2019-07-26 20:59:14 +00:00
x = median3(x, y, z);
//int x = v[I[start + len / 2] + h];
2019-09-14 04:35:42 +00:00
var jj = 0;
var kk = 0;
for (var i2 = start; i2 < start + len; i2++)
2019-07-21 22:47:17 +00:00
{
if (v[I[i2] + h] < x)
jj++;
if (v[I[i2] + h] == x)
kk++;
}
2019-09-14 04:35:42 +00:00
2019-07-21 22:47:17 +00:00
jj += start;
kk += jj;
2019-09-14 04:35:42 +00:00
var i = start;
2019-07-26 20:59:14 +00:00
j = 0;
k = 0;
2019-07-21 22:47:17 +00:00
while (i < jj)
if (v[I[i] + h] < x)
{
i++;
}
else if (v[I[i] + h] == x)
{
Swap(ref I[i], ref I[jj + j]);
j++;
}
else
{
Swap(ref I[i], ref I[kk + k]);
k++;
}
while (jj + j < kk)
if (v[I[jj + j] + h] == x)
{
j++;
}
else
{
Swap(ref I[jj + j], ref I[kk + k]);
k++;
}
if (jj > start)
Split(I, v, start, jj - start, h);
for (i = 0; i < kk - jj; i++)
v[I[jj + i]] = kk - 1;
if (jj == kk - 1)
I[jj] = -1;
if (start + len > kk)
Split(I, v, kk, start + len - kk, h);
}
}
private static int[] SuffixSort(byte[] oldData)
{
2019-09-14 04:35:42 +00:00
var buckets = new int[256];
2019-07-21 22:47:17 +00:00
2019-09-14 04:35:42 +00:00
foreach (var oldByte in oldData)
2019-07-21 22:47:17 +00:00
buckets[oldByte]++;
2019-09-14 04:35:42 +00:00
for (var i = 1; i < 256; i++)
2019-07-21 22:47:17 +00:00
buckets[i] += buckets[i - 1];
2019-09-14 04:35:42 +00:00
for (var i = 255; i > 0; i--)
2019-07-21 22:47:17 +00:00
buckets[i] = buckets[i - 1];
buckets[0] = 0;
2019-09-14 04:35:42 +00:00
var I = new int[oldData.Length + 1];
for (var i = 0; i < oldData.Length; i++)
2019-07-21 22:47:17 +00:00
I[++buckets[oldData[i]]] = i;
2019-09-14 04:35:42 +00:00
var v = new int[oldData.Length + 1];
for (var i = 0; i < oldData.Length; i++)
2019-07-21 22:47:17 +00:00
v[i] = buckets[oldData[i]];
2019-09-14 04:35:42 +00:00
for (var i = 1; i < 256; i++)
2019-07-21 22:47:17 +00:00
if (buckets[i] == buckets[i - 1] + 1)
I[buckets[i]] = -1;
I[0] = -1;
2019-09-14 04:35:42 +00:00
for (var h = 1; I[0] != -(oldData.Length + 1); h += h)
2019-07-21 22:47:17 +00:00
{
2019-09-14 04:35:42 +00:00
var len = 0;
var i = 0;
2019-07-21 22:47:17 +00:00
while (i < oldData.Length + 1)
if (I[i] < 0)
{
len -= I[i];
i -= I[i];
}
else
{
if (len != 0)
I[i - len] = -len;
len = v[I[i]] + 1 - i;
Split(I, v, i, len, h);
i += len;
len = 0;
}
if (len != 0)
I[i - len] = -len;
}
2019-09-14 04:35:42 +00:00
for (var i = 0; i < oldData.Length + 1; i++)
2019-07-21 22:47:17 +00:00
I[v[i]] = i;
return I;
}
private static void Swap(ref int first, ref int second)
{
2019-09-14 04:35:42 +00:00
var temp = first;
2019-07-21 22:47:17 +00:00
first = second;
second = temp;
}
private static long ReadInt64(byte[] buf, int offset)
{
long value = buf[offset + 7] & 0x7F;
2019-09-14 04:35:42 +00:00
for (var index = 6; index >= 0; index--)
2019-07-21 22:47:17 +00:00
{
value *= 256;
value += buf[offset + index];
}
if ((buf[offset + 7] & 0x80) != 0)
value = -value;
return value;
}
private static void WriteInt64(long value, byte[] buf, int offset)
{
2019-09-14 04:35:42 +00:00
var valueToWrite = value < 0 ? -value : value;
2019-07-21 22:47:17 +00:00
2019-09-14 04:35:42 +00:00
for (var byteIndex = 0; byteIndex < 8; byteIndex++)
2019-07-21 22:47:17 +00:00
{
2019-09-14 04:35:42 +00:00
buf[offset + byteIndex] = unchecked((byte) valueToWrite);
2019-07-21 22:47:17 +00:00
valueToWrite >>= 8;
}
if (value < 0)
buf[offset + 7] |= 0x80;
}
/// <summary>
2019-09-14 04:35:42 +00:00
/// Reads exactly <paramref name="count" /> bytes from <paramref name="stream" />.
2019-07-21 22:47:17 +00:00
/// </summary>
/// <param name="stream">The stream to read from.</param>
/// <param name="count">The count of bytes to read.</param>
/// <returns>A new byte array containing the data read from the stream.</returns>
private static byte[] ReadExactly(Stream stream, int count)
{
if (count < 0)
throw new ArgumentOutOfRangeException("count");
2019-09-14 04:35:42 +00:00
var buffer = new byte[count];
2019-07-21 22:47:17 +00:00
ReadExactly(stream, buffer, 0, count);
return buffer;
}
/// <summary>
2019-09-14 04:35:42 +00:00
/// Reads exactly <paramref name="count" /> bytes from <paramref name="stream" /> into
/// <paramref name="buffer" />, starting at the byte given by <paramref name="offset" />.
2019-07-21 22:47:17 +00:00
/// </summary>
/// <param name="stream">The stream to read from.</param>
/// <param name="buffer">The buffer to read data into.</param>
/// <param name="offset">The offset within the buffer at which data is first written.</param>
/// <param name="count">The count of bytes to read.</param>
private static void ReadExactly(Stream stream, byte[] buffer, int offset, int count)
{
// check arguments
if (stream == null)
throw new ArgumentNullException("stream");
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset < 0 || offset > buffer.Length)
throw new ArgumentOutOfRangeException("offset");
if (count < 0 || buffer.Length - offset < count)
throw new ArgumentOutOfRangeException("count");
while (count > 0)
{
// read data
2019-09-14 04:35:42 +00:00
var bytesRead = stream.Read(buffer, offset, count);
2019-07-21 22:47:17 +00:00
// check for failure to read
if (bytesRead == 0)
throw new EndOfStreamException();
// move to next block
offset += bytesRead;
count -= bytesRead;
}
}
2019-07-26 20:59:14 +00:00
private static int median3(int a, int b, int c)
{
2019-09-14 04:35:42 +00:00
return a < b ? b < c ? b : a < c ? c : a : b > c ? b : a > c ? c : a;
2019-07-26 20:59:14 +00:00
}
2019-07-21 22:47:17 +00:00
}
2019-09-14 04:35:42 +00:00
}