context: namespace Maticsoft.ZipLib.Zip.Compression
  {
      using Maticsoft.ZipLib.Checksums;
      using System;
  
      public class DeflaterEngine : DeflaterConstants
      {
          private Adler32 adler;
          private int blockStart;
          private int compressionFunction;
          private int goodLength;
          private short[] head;
          private DeflaterHuffman huffman;
          private byte[] inputBuf;
          private int inputEnd;
          private int inputOff;
          private int ins_h;
          private int lookahead;
          private int matchLen;
          private int matchStart;
          private int max_chain;
          private int max_lazy;
          private int niceLength;
          private DeflaterPending pending;
          private short[] prev;
          private bool prevAvailable;
          private DeflateStrategy strategy;
          private int strstart;
          private const int TooFar = 0x1000;
          private long totalIn;
          private byte[] window;
  
          public DeflaterEngine(DeflaterPending pending)
          {
              this.pending = pending;
              this.huffman = new DeflaterHuffman(pending);
              this.adler = new Adler32();
              this.window = new byte[0x10000];
              this.head = new short[0x8000];
              this.prev = new short[0x8000];
              this.blockStart = this.strstart = 1;
          }
  
          public bool Deflate(bool flush, bool finish)
          {
              bool flag;
              do
              {
                  this.FillWindow();
                  bool flag2 = flush && (this.inputOff == this.inputEnd);
                  switch (this.compressionFunction)
                  {
                      case 0:
                          flag = this.DeflateStored(flag2, finish);
                          break;
  
                      case 1:
                          flag = this.DeflateFast(flag2, finish);
                          break;
  
                      case 2:
                          flag = this.DeflateSlow(flag2, finish);
                          break;
  
                      default:
                          throw new InvalidOperationException("unknown compressionFunction");
                  }
              }
              while (this.pending.IsFlushed && flag);
              return flag;
          }
  
          private bool DeflateFast(bool flush, bool finish)
          {
              if (!((this.lookahead >= 0x106) || flush))
              {
                  return false;
              }
              while ((this.lookahead >= 0x106) || flush)
              {
                  int num;
                  if (this.lookahead == 0)
                  {
                      this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, finish);
                      this.blockStart = this.strstart;
                      return false;
                  }
                  if (this.strstart > 0xfefa)
                  {
                      this.SlideWindow();
                  }
                  if ((((this.lookahead >= 3) && ((num = this.InsertString()) != 0)) && ((this.strategy != DeflateStrategy.HuffmanOnly) && ((this.strstart - num) <= 0x7efa))) && this.FindLongestMatch(num))
                  {
                      bool flag = this.huffman.TallyDist(this.strstart - this.matchStart, this.matchLen);
                      this.lookahead -= this.matchLen;
                      if ((this.matchLen <= this.max_lazy) && (this.lookahead >= 3))
                      {
                          while (--this.matchLen > 0)
                          {
                              this.strstart++;
                              this.InsertString();
                          }
                          this.strstart++;
                      }
                      else
                      {
                          this.strstart += this.matchLen;
                          if (this.lookahead >= 2)
                          {
                              this.UpdateHash();
                          }
                      }
                      this.matchLen = 2;
                      if (!flag)
                      {
                          continue;
                      }
                  }
                  else
                  {
                      this.huffman.TallyLit(this.window[this.strstart] & 0xff);
                      this.strstart++;
                      this.lookahead--;
                  }
                  if (this.huffman.IsFull())
                  {
                      bool lastBlock = finish && (this.lookahead == 0);
                      this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, lastBlock);
                      this.blockStart = this.strstart;
                      return !lastBlock;
                  }
              }
              return true;
          }
  
          private bool DeflateSlow(bool flush, bool finish)
          {
              if (!((this.lookahead >= 0x106) || flush))
              {
                  return false;
              }
              while ((this.lookahead >= 0x106) || flush)
              {
                  if (this.lookahead == 0)
                  {
                      if (this.prevAvailable)
                      {
                          this.huffman.TallyLit(this.window[this.strstart - 1] & 0xff);
                      }
                      this.prevAvailable = false;
                      this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, finish);
                      this.blockStart = this.strstart;
                      return false;
                  }
                  if (this.strstart >= 0xfefa)
                  {
                      this.SlideWindow();
                  }
                  int matchStart = this.matchStart;
                  int matchLen = this.matchLen;
                  if (this.lookahead >= 3)
                  {
                      int curMatch = this.InsertString();
                      if (((((this.strategy != DeflateStrategy.HuffmanOnly) && (curMatch != 0)) && ((this.strstart - curMatch) <= 0x7efa)) && this.FindLongestMatch(curMatch)) && ((this.matchLen <= 5) && ((this.strategy == DeflateStrategy.Filtered) || ((this.matchLen == 3) && ((this.strstart - this.matchStart) > 0x1000)))))
                      {
                          this.matchLen = 2;
                      }
                  }
                  if ((matchLen >= 3) && (this.matchLen <= matchLen))
                  {
                      this.huffman.TallyDist((this.strstart - 1) - matchStart, matchLen);
                      matchLen -= 2;
                      do
                      {
                          this.strstart++;
                          this.lookahead--;
                          if (this.lookahead >= 3)
                          {
                              this.InsertString();
                          }
                      }
                      while (--matchLen > 0);
                      this.strstart++;
                      this.lookahead--;
                      this.prevAvailable = false;
                      this.matchLen = 2;
                  }
                  else
                  {
                      if (this.prevAvailable)
                      {
                          this.huffman.TallyLit(this.window[this.strstart - 1] & 0xff);
                      }
                      this.prevAvailable = true;
                      this.strstart++;
                      this.lookahead--;
                  }
                  if (this.huffman.IsFull())
                  {
                      int storedLength = this.strstart - this.blockStart;
                      if (this.prevAvailable)
                      {
                          storedLength--;
                      }
                      bool lastBlock = (finish && (this.lookahead == 0)) && !this.prevAvailable;
                      this.huffman.FlushBlock(this.window, this.blockStart, storedLength, lastBlock);
                      this.blockStart += storedLength;
                      return !lastBlock;
                  }
              }
              return true;
          }
  
          private bool DeflateStored(bool flush, bool finish)
          {
              if (!(flush || (this.lookahead != 0)))
              {
                  return false;
              }
              this.strstart += this.lookahead;
              this.lookahead = 0;
              int storedLength = this.strstart - this.blockStart;
              if (((storedLength >= DeflaterConstants.MAX_BLOCK_SIZE) || ((this.blockStart < 0x8000) && (storedLength >= 0x7efa))) || flush)
              {
                  bool lastBlock = finish;
                  if (storedLength > DeflaterConstants.MAX_BLOCK_SIZE)
                  {
                      storedLength = DeflaterConstants.MAX_BLOCK_SIZE;
                      lastBlock = false;
                  }
                  this.huffman.FlushStoredBlock(this.window, this.blockStart, storedLength, lastBlock);
                  this.blockStart += storedLength;
                  return !lastBlock;
              }
              return true;
          }
  
          public void FillWindow()
          {
              if (this.strstart >= 0xfefa)
              {
                  this.SlideWindow();
              }
              while ((this.lookahead < 0x106) && (this.inputOff < this.inputEnd))
              {
                  int length = (0x10000 - this.lookahead) - this.strstart;
                  if (length > (this.inputEnd - this.inputOff))
                  {
                      length = this.inputEnd - this.inputOff;
                  }
                  Array.Copy(this.inputBuf, this.inputOff, this.window, this.strstart + this.lookahead, length);
                  this.adler.Update(this.inputBuf, this.inputOff, length);
                  this.inputOff += length;
                  this.totalIn += length;
                  this.lookahead += length;
              }
              if (this.lookahead >= 3)
              {
                  this.UpdateHash();
              }
          }
  
          private bool FindLongestMatch(int curMatch)
          {
              int num = this.max_chain;
              int niceLength = this.niceLength;
              short[] prev = this.prev;
              int strstart = this.strstart;
              int index = this.strstart + this.matchLen;
              int num6 = Math.Max(this.matchLen, 2);
  
gt:             int num7 = Math.Max(this.strstart - 0x7efa, 0);