using System;
namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
{
///
/// Contains the output from the Inflation process.
/// We need to have a window so that we can refer backwards into the output stream
/// to repeat stuff.
/// Author of the original java version : John Leuner
///
public class OutputWindow
{
#region Constants
const int WindowSize = 1 << 15;
const int WindowMask = WindowSize - 1;
#endregion
#region Instance Fields
byte[] window = new byte[WindowSize]; //The window is 2^15 bytes
int windowEnd;
int windowFilled;
#endregion
///
/// Write a byte to this output window
///
/// value to write
///
/// if window is full
///
public void Write(int value)
{
if (windowFilled++ == WindowSize) {
throw new InvalidOperationException("Window full");
}
window[windowEnd++] = (byte)value;
windowEnd &= WindowMask;
}
private void SlowRepeat(int repStart, int length, int distance)
{
while (length-- > 0) {
window[windowEnd++] = window[repStart++];
windowEnd &= WindowMask;
repStart &= WindowMask;
}
}
///
/// Append a byte pattern already in the window itself
///
/// length of pattern to copy
/// distance from end of window pattern occurs
///
/// If the repeated data overflows the window
///
public void Repeat(int length, int distance)
{
if ((windowFilled += length) > WindowSize) {
throw new InvalidOperationException("Window full");
}
int repStart = (windowEnd - distance) & WindowMask;
int border = WindowSize - length;
if ((repStart <= border) && (windowEnd < border)) {
if (length <= distance) {
System.Array.Copy(window, repStart, window, windowEnd, length);
windowEnd += length;
} else {
// We have to copy manually, since the repeat pattern overlaps.
while (length-- > 0) {
window[windowEnd++] = window[repStart++];
}
}
} else {
SlowRepeat(repStart, length, distance);
}
}
///
/// Copy from input manipulator to internal window
///
/// source of data
/// length of data to copy
/// the number of bytes copied
public int CopyStored(StreamManipulator input, int length)
{
length = Math.Min(Math.Min(length, WindowSize - windowFilled), input.AvailableBytes);
int copied;
int tailLen = WindowSize - windowEnd;
if (length > tailLen) {
copied = input.CopyBytes(window, windowEnd, tailLen);
if (copied == tailLen) {
copied += input.CopyBytes(window, 0, length - tailLen);
}
} else {
copied = input.CopyBytes(window, windowEnd, length);
}
windowEnd = (windowEnd + copied) & WindowMask;
windowFilled += copied;
return copied;
}
///
/// Copy dictionary to window
///
/// source dictionary
/// offset of start in source dictionary
/// length of dictionary
///
/// If window isnt empty
///
public void CopyDict(byte[] dictionary, int offset, int length)
{
if (dictionary == null) {
throw new ArgumentNullException("nameof(dictionary)");
}
if (windowFilled > 0) {
throw new InvalidOperationException();
}
if (length > WindowSize) {
offset += length - WindowSize;
length = WindowSize;
}
System.Array.Copy(dictionary, offset, window, 0, length);
windowEnd = length & WindowMask;
}
///
/// Get remaining unfilled space in window
///
/// Number of bytes left in window
public int GetFreeSpace()
{
return WindowSize - windowFilled;
}
///
/// Get bytes available for output in window
///
/// Number of bytes filled
public int GetAvailable()
{
return windowFilled;
}
///
/// Copy contents of window to output
///
/// buffer to copy to
/// offset to start at
/// number of bytes to count
/// The number of bytes copied
///
/// If a window underflow occurs
///
public int CopyOutput(byte[] output, int offset, int len)
{
int copyEnd = windowEnd;
if (len > windowFilled) {
len = windowFilled;
} else {
copyEnd = (windowEnd - windowFilled + len) & WindowMask;
}
int copied = len;
int tailLen = len - copyEnd;
if (tailLen > 0) {
System.Array.Copy(window, WindowSize - tailLen, output, offset, tailLen);
offset += tailLen;
len = copyEnd;
}
System.Array.Copy(window, copyEnd - len, output, offset, len);
windowFilled -= copied;
if (windowFilled < 0) {
throw new InvalidOperationException();
}
return copied;
}
///
/// Reset by clearing window so GetAvailable returns 0
///
public void Reset()
{
windowFilled = windowEnd = 0;
}
}
}