context: // OutputWindow.cs
  //
  // Copyright (C) 2001 Mike Krueger
  //
  // This file was translated from java, it was part of the GNU Classpath
  // Copyright (C) 2001 Free Software Foundation, Inc.
  //
  // This program is free software; you can redistribute it and/or
  // modify it under the terms of the GNU General Public License
  // as published by the Free Software Foundation; either version 2
  // of the License, or (at your option) any later version.
  //
  // This program is distributed in the hope that it will be useful,
  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  // GNU General Public License for more details.
  //
  // You should have received a copy of the GNU General Public License
  // along with this program; if not, write to the Free Software
  // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  //
  // Linking this library statically or dynamically with other modules is
  // making a combined work based on this library.  Thus, the terms and
  // conditions of the GNU General Public License cover the whole
  // combination.
  // 
  // As a special exception, the copyright holders of this library give you
  // permission to link this library with independent modules to produce an
  // executable, regardless of the license terms of these independent
  // modules, and to copy and distribute the resulting executable under
  // terms of your choice, provided that you also meet, for each linked
  // independent module, the terms and conditions of the license of that
  // module.  An independent module is a module which is not derived from
  // or based on this library.  If you modify this library, you may extend
  // this exception to your version of the library, but you are not
  // obligated to do so.  If you do not wish to do so, delete this
  // exception statement from your version.
  
  using System;
  
  namespace Creek.Compression.Zip.Compression.Streams 
  {
  	
  	/// <summary>
  	/// 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.<br/>
  	/// Author of the original java version : John Leuner
  	/// </summary>
  	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
  		
  		/// <summary>
  		/// Write a byte to this output window
  		/// </summary>
  		/// <param name="value">value to write</param>
  		/// <exception cref="InvalidOperationException">
  		/// if window is full
  		/// </exception>
  		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;
  			}
  		}
  		
  		/// <summary>
  		/// Append a byte pattern already in the window itself
  		/// </summary>
  		/// <param name="length">length of pattern to copy</param>
  		/// <param name="distance">distance from end of window pattern occurs</param>
  		/// <exception cref="InvalidOperationException">
  		/// If the repeated data overflows the window
  		/// </exception>
  		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);
  			}
  		}
  		
  		/// <summary>
  		/// Copy from input manipulator to internal window
  		/// </summary>
  		/// <param name="input">source of data</param>
  		/// <param name="length">length of data to copy</param>
  		/// <returns>the number of bytes copied</returns>
  		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;
  		}
  		
  		/// <summary>
  		/// Copy dictionary to window
  		/// </summary>
  		/// <param name="dictionary">source dictionary</param>
  		/// <param name="offset">offset of start in source dictionary</param>
  		/// <param name="length">length of dictionary</param>
  		/// <exception cref="InvalidOperationException">
  		/// If window isnt empty
  		/// </exception>
  		public void CopyDict(byte[] dictionary, int offset, int length)
  		{
  			if ( dictionary == null ) {
  				throw new ArgumentNullException("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;
  		}
  
  		/// <summary>
  		/// Get remaining unfilled space in window
  		/// </summary>
  		/// <returns>Number of bytes left in window</returns>
  		public int GetFreeSpace()
  		{
  			return WindowSize - windowFilled;
  		}
  		
  		/// <summary>
  		/// Get bytes available for output in window
  		/// </summary>
  		/// <returns>Number of bytes filled</returns>
  		public int GetAvailable()
  		{
  			return windowFilled;
  		}
  
  		/// <summary>
  		/// Copy contents of window to output
  		/// </summary>
  		/// <param name="output">buffer to copy to</param>
  		/// <param name="offset">offset to start at</param>
  		/// <param name="len">number of bytes to count</param>
  		/// <returns>The number of bytes copied</returns>
  		/// <exception cref="InvalidOperationException">
  		/// If a window underflow occurs
  		/// </exception>
  		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;
  
gt: 			if (windowFilled < 0) {