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 Fomm.SharpZipLib.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>
    internal class OutputWindow
    {
      #region Constants
  
      private const int WindowSize = 1 << 15;
      private const int WindowMask = WindowSize - 1;
  
      #endregion
  
      #region Instance Fields
  
      private byte[] window = new byte[WindowSize]; //The window is 2^15 bytes
      private int windowEnd;
      private 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)
      {
        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");
        }
  
        var repStart = (windowEnd - distance) & WindowMask;
        var border = WindowSize - length;
        if ((repStart <= border) && (windowEnd < border))
        {
          if (length <= distance)
          {
            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);
        }
      }
  
      /// <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;
  
        var 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>
      ///   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)
      {
        var copyEnd = windowEnd;
        if (len > windowFilled)
        {
          len = windowFilled;
        }
        else
        {
          copyEnd = (windowEnd - windowFilled + len) & WindowMask;
        }
  
        var copied = len;
        var tailLen = len - copyEnd;
  
        if (tailLen > 0)
        {
          Array.Copy(window, WindowSize - tailLen, output, offset, tailLen);
          offset += tailLen;
          len = copyEnd;
        }
        Array.Copy(window, copyEnd - len, output, offset, len);
        windowFilled -= copied;
  
gt:       if (windowFilled < 0)