The Labs \ Source Viewer \ SSCLI \ System.IO.Compression \ OutputWindow

  1. //------------------------------------------------------------------------------
  2. // <copyright file="OutputWindow.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. namespace System.IO.Compression
  16. {
  17.     using System;
  18.     using System.Diagnostics;
  19.     using System.Globalization;
  20.    
  21.     // This class maintains a window for decompressed output.
  22.     // We need to keep this because the decompressed information can be
  23.     // a literal or a length/distance pair. For length/distance pair,
  24.     // we need to look back in the output window and copy bytes from there.
  25.     // We use a byte array of WindowSize circularly.
  26.     //
  27.     internal class OutputWindow
  28.     {
  29.        
  30.         private const int WindowSize = 32768;
  31.         private const int WindowMask = 32767;
  32.        
  33.         private byte[] window = new byte[WindowSize];
  34.         //The window is 2^15 bytes
  35.         private int end;
  36.         // this is the position to where we should write next byte
  37.         private int bytesUsed;
  38.         // The number of bytes in the output window which is not consumed.
  39.         // Add a byte to output window
  40.         public void Write(byte b)
  41.         {
  42.             Debug.WriteLineIf(CompressionTracingSwitch.Verbose, String.Format(CultureInfo.InvariantCulture, "Literal: {0}", b), "Compression");
  43.             Debug.Assert(bytesUsed < WindowSize, "Can't add byte when window is full!");
  44.             window[end++] = b;
  45.             end &= WindowMask;
  46.             ++bytesUsed;
  47.         }
  48.        
  49.         public void WriteLengthDistance(int length, int distance)
  50.         {
  51.             Debug.WriteLineIf(CompressionTracingSwitch.Verbose, String.Format(CultureInfo.InvariantCulture, "Length/Distance: {0}:{1}", length, distance), "Compression");
  52.             Debug.Assert((bytesUsed + length) <= WindowSize, "No Enough space");
  53.            
  54.             // move backwards distance bytes in the output stream,
  55.             // and copy length bytes from this position to the output stream.
  56.             bytesUsed += length;
  57.             int copyStart = (end - distance) & WindowMask;
  58.             // start position for coping.
  59.             int border = WindowSize - length;
  60.             if (copyStart <= border && end < border) {
  61.                 if (length <= distance) {
  62.                     System.Array.Copy(window, copyStart, window, end, length);
  63.                     end += length;
  64.                 }
  65.                 else {
  66.                     // The referenced string may overlap the current
  67.                     // position; for example, if the last 2 bytes decoded have values
  68.                     // X and Y, a string reference with <length = 5, distance = 2>
  69.                     // adds X,Y,X,Y,X to the output stream.
  70.                     while (length-- > 0) {
  71.                         window[end++] = window[copyStart++];
  72.                     }
  73.                 }
  74.             }
  75.             else {
  76.                 // copy byte by byte
  77.                 while (length-- > 0) {
  78.                     window[end++] = window[copyStart++];
  79.                     end &= WindowMask;
  80.                     copyStart &= WindowMask;
  81.                 }
  82.             }
  83.         }
  84.        
  85.         // Copy up to length of bytes from input directly.
  86.         // This is used for uncompressed block.
  87.         public int CopyFrom(InputBuffer input, int length)
  88.         {
  89.             length = Math.Min(Math.Min(length, WindowSize - bytesUsed), input.AvailableBytes);
  90.             int copied;
  91.            
  92.             // We might need wrap around to copy all bytes.
  93.             int tailLen = WindowSize - end;
  94.             if (length > tailLen) {
  95.                 // copy the first part
  96.                 copied = input.CopyTo(window, end, tailLen);
  97.                 if (copied == tailLen) {
  98.                     // only try to copy the second part if we have enough bytes in input
  99.                     copied += input.CopyTo(window, 0, length - tailLen);
  100.                 }
  101.             }
  102.             else {
  103.                 // only one copy is needed if there is no wrap around.
  104.                 copied = input.CopyTo(window, end, length);
  105.             }
  106.            
  107.             end = (end + copied) & WindowMask;
  108.             bytesUsed += copied;
  109.             return copied;
  110.         }
  111.        
  112.         // Free space in output window
  113.         public int FreeBytes {
  114.             get { return WindowSize - bytesUsed; }
  115.         }
  116.        
  117.         // bytes not consumed in output window
  118.         public int AvailableBytes {
  119.             get { return bytesUsed; }
  120.         }
  121.        
  122.         // copy the decompressed bytes to output array.
  123.         public int CopyTo(byte[] output, int offset, int length)
  124.         {
  125.             int copy_end;
  126.            
  127.             if (length > bytesUsed) {
  128.                 // we can copy all the decompressed bytes out
  129.                 copy_end = end;
  130.                 length = bytesUsed;
  131.             }
  132.             else {
  133.                 copy_end = (end - bytesUsed + length) & WindowMask;
  134.                 // copy length of bytes
  135.             }
  136.            
  137.             int copied = length;
  138.            
  139.             int tailLen = length - copy_end;
  140.             if (tailLen > 0) {
  141.                 // this means we need to copy two parts seperately
  142.                 // copy tailLen bytes from the end of output window
  143.                 System.Array.Copy(window, WindowSize - tailLen, output, offset, tailLen);
  144.                 offset += tailLen;
  145.                 length = copy_end;
  146.             }
  147.             System.Array.Copy(window, copy_end - length, output, offset, length);
  148.             bytesUsed -= copied;
  149.             Debug.Assert(bytesUsed >= 0, "check this function and find why we copied more bytes than we have");
  150.             return copied;
  151.         }
  152.     }
  153. }

Developer Fusion