// File: drummyfish[P]flowchart/src/lodepng.cpp
// Function: encodeLZ77(uivector*, Hash*, unsigned char const*, unsigned long, unsigned long, unsigned int)
static unsigned encodeLZ77(uivector* out, Hash* hash,
                           const unsigned char* in, size_t inpos, size_t insize, unsigned windowsize)
{
  unsigned short numzeros = 0;
  int usezeros = windowsize >= 8192; /*for small window size, the 'max chain length' optimization does a better job*/
  unsigned pos, i, error = 0;

  if(!error)
  {
    unsigned offset; /*the offset represents the distance in LZ77 terminology*/
    unsigned length;
#ifdef LAZY_MATCHING
    unsigned lazy = 0;
    unsigned lazylength = 0, lazyoffset = 0;
#endif /*LAZY_MATCHING*/
    unsigned hashval;
    unsigned current_offset, current_length;
    const unsigned char *lastptr, *foreptr, *backptr;
    unsigned short hashpos, prevpos;

    for(pos = inpos; pos < insize; pos++)
    {
      size_t wpos = pos % windowsize; /*position for in 'circular' hash buffers*/

      hashval = getHash(in, insize, pos);
      updateHashChain(hash, pos, hashval, windowsize);

      if(usezeros && hashval == 0)
      {
        numzeros = countZeros(in, insize, pos);
        hash->zeros[wpos] = numzeros;
      }

      /*the length and offset found for the current position*/
      length = 0;
      offset = 0;

      prevpos = hash->head[hashval];
      hashpos = hash->chain[prevpos];

      lastptr = &in[insize < pos + MAX_SUPPORTED_DEFLATE_LENGTH ? insize : pos + MAX_SUPPORTED_DEFLATE_LENGTH];

      /*search for the longest string*/
      if(hash->val[wpos] == (int)hashval)
      {
        /*for large window lengths, assume the user wants no compression loss. Otherwise, max hash chain length speedup.*/
        unsigned maxchainlength = windowsize >= 8192 ? windowsize : windowsize / 8;
        for(;;)
        {
          /*stop when went completely around the circular buffer*/
          if(prevpos < wpos && hashpos > prevpos && hashpos <= wpos) break;
          if(prevpos > wpos && (hashpos <= wpos || hashpos > prevpos)) break;
          if(maxchainlength-- == 0) break;

          current_offset = hashpos <= wpos ? wpos - hashpos : wpos - hashpos + windowsize;
          if(current_offset > 0)
          {
            /*test the next characters*/
            foreptr = &in[pos];
            backptr = &in[pos - current_offset];

            /*common case in PNGs is lots of zeros. Quickly skip over them as a speedup*/
            if(usezeros && hashval == 0 && hash->val[hashpos] == 0 /*hashval[hashpos] may be out of date*/)
            {
              unsigned short skip = hash->zeros[hashpos];
              if(skip > numzeros) skip = numzeros;
              backptr += skip;
              foreptr += skip;
            }

            /* multiple checks at once per array bounds check */
            while(foreptr != lastptr && *backptr == *foreptr) /*maximum supported length by deflate is max length*/
            {
              ++backptr;
              ++foreptr;
            }
            current_length = (unsigned)(foreptr - &in[pos]);

            if(current_length > length)
            {
              length = current_length; /*the longest length*/
              offset = current_offset; /*the offset that is related to this longest length*/
              /*jump out once a length of max length is found (speed gain)*/
              if(current_length == MAX_SUPPORTED_DEFLATE_LENGTH) break;
            }
          }

          if(hashpos == hash->chain[hashpos]) break;

          prevpos = hashpos;
          hashpos = hash->chain[hashpos];
        }
      }

#ifdef LAZY_MATCHING
      if(!lazy && length >= 3 && length < MAX_SUPPORTED_DEFLATE_LENGTH)
      {
        lazy = 1;
        lazylength = length;
        lazyoffset = offset;
        continue;
      }
      if(lazy)
      {
        lazy = 0;
        if(pos == 0) ERROR_BREAK(81);
        if(length > lazylength + 1)
        {
          /*push the previous character as literal*/
          if(!uivector_push_back(out, in[pos - 1])) ERROR_BREAK(83 /*alloc fail*/);
        }
        else
        {
          length = lazylength;
          offset = lazyoffset;
          hash->head[hashval] = -1; /*the same hashchain update will be done, this ensures no wrong alteration*/
          pos--;
        }
      }
#endif /*LAZY_MATCHING*/

      if(length >= 3 && offset > windowsize) ERROR_BREAK(86 /*too big (or overflown negative) offset*/);


      /**encode it as length/distance pair or literal value**/
      if(length < 3) /*only lengths of 3 or higher are supported as length/distance pair*/
      {
        if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/);
      }
      else
      {
        if(length == 3 && offset > 2048)
        {
          /*compensate for the fact that longer offsets have more extra bits, a
          length of only 3 may be not worth it then*/
          if(!uivector_push_back(out, in[pos + 0])) ERROR_BREAK(83 /*alloc fail*/);
          if(!uivector_push_back(out, in[pos + 1])) ERROR_BREAK(83 /*alloc fail*/);
          if(!uivector_push_back(out, in[pos + 2])) ERROR_BREAK(83 /*alloc fail*/);
        }
        else
        {
          addLengthDistance(out, length, offset);
        }
        for(i = 1; i < length; i++)
        {
          pos++;
          hashval = getHash(in, insize, pos);
          updateHashChain(hash, pos, hashval, windowsize);
          if(usezeros && hashval == 0)
          {
            hash->zeros[pos % windowsize] = countZeros(in, insize, pos);
          }
        }
      }

    } /*end of the loop through each character of input*/
  } /*end of "if(!error)"*/

  return error;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0xc0, %rsp
movq %rdi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
movq %rdx, -0x18(%rbp)
movq %rcx, -0x20(%rbp)
movq %r8, -0x28(%rbp)
movl %r9d, -0x2c(%rbp)
movw $0x0, -0x2e(%rbp)
cmpl $0x2000, -0x2c(%rbp)    # imm = 0x2000
setae %al
andb $0x1, %al
movzbl %al, %eax
movl %eax, -0x34(%rbp)
movl $0x0, -0x40(%rbp)
cmpl $0x0, -0x40(%rbp)
jne 0xfdc7
movl $0x0, -0x4c(%rbp)
movl $0x0, -0x50(%rbp)
movl $0x0, -0x54(%rbp)
movq -0x20(%rbp), %rax
movl %eax, -0x38(%rbp)
movl -0x38(%rbp), %eax
cmpq -0x28(%rbp), %rax
jae 0xfdc5
movl -0x38(%rbp), %eax
xorl %edx, %edx
divl -0x2c(%rbp)
movl %edx, %eax
movq %rax, -0x88(%rbp)
movq -0x18(%rbp), %rdi
movq -0x28(%rbp), %rsi
movl -0x38(%rbp), %eax
movl %eax, %edx
callq 0x10010
movl %eax, -0x58(%rbp)
movq -0x10(%rbp), %rdi
movl -0x38(%rbp), %eax
movl %eax, %esi
movl -0x58(%rbp), %edx
movl -0x2c(%rbp), %ecx
callq 0x100b0
cmpl $0x0, -0x34(%rbp)
je 0xf878
cmpl $0x0, -0x58(%rbp)
jne 0xf878
movq -0x18(%rbp), %rdi
movq -0x28(%rbp), %rsi
movl -0x38(%rbp), %eax
movl %eax, %edx
callq 0x10130
movw %ax, -0x2e(%rbp)
movw -0x2e(%rbp), %dx
movq -0x10(%rbp), %rax
movq 0x18(%rax), %rax
movq -0x88(%rbp), %rcx
movw %dx, (%rax,%rcx,2)
movl $0x0, -0x48(%rbp)
movl $0x0, -0x44(%rbp)
movq -0x10(%rbp), %rax
movq (%rax), %rax
movl -0x58(%rbp), %ecx
movl (%rax,%rcx,4), %eax
movw %ax, -0x7c(%rbp)
movq -0x10(%rbp), %rax
movq 0x10(%rax), %rax
movzwl -0x7c(%rbp), %ecx
movw (%rax,%rcx,2), %ax
movw %ax, -0x7a(%rbp)
movq -0x18(%rbp), %rax
movq %rax, -0x98(%rbp)
movq -0x28(%rbp), %rax
movl -0x38(%rbp), %ecx
addq $0x102, %rcx            # imm = 0x102
cmpq %rcx, %rax
jae 0xf8d6
movq -0x28(%rbp), %rax
movq %rax, -0xa0(%rbp)
jmp 0xf8e6
movl -0x38(%rbp), %eax
addq $0x102, %rax            # imm = 0x102
movq %rax, -0xa0(%rbp)
movq -0x98(%rbp), %rax
movq -0xa0(%rbp), %rcx
addq %rcx, %rax
movq %rax, -0x68(%rbp)
movq -0x10(%rbp), %rax
movq 0x8(%rax), %rax
movq -0x88(%rbp), %rcx
movl (%rax,%rcx,4), %eax
cmpl -0x58(%rbp), %eax
jne 0xfb69
cmpl $0x2000, -0x2c(%rbp)    # imm = 0x2000
jb 0xf92a
movl -0x2c(%rbp), %eax
movl %eax, -0xa4(%rbp)
jmp 0xf936
movl -0x2c(%rbp), %eax
shrl $0x3, %eax
movl %eax, -0xa4(%rbp)
movl -0xa4(%rbp), %eax
movl %eax, -0x8c(%rbp)
movzwl -0x7c(%rbp), %eax
cmpq -0x88(%rbp), %rax
jae 0xf96d
movzwl -0x7a(%rbp), %eax
movzwl -0x7c(%rbp), %ecx
cmpl %ecx, %eax
jle 0xf96d
movzwl -0x7a(%rbp), %eax
cmpq -0x88(%rbp), %rax
ja 0xf96d
jmp 0xfb67
movzwl -0x7c(%rbp), %eax
cmpq -0x88(%rbp), %rax
jbe 0xf998
movzwl -0x7a(%rbp), %eax
cmpq -0x88(%rbp), %rax
jbe 0xf993
movzwl -0x7a(%rbp), %eax
movzwl -0x7c(%rbp), %ecx
cmpl %ecx, %eax
jle 0xf998
jmp 0xfb67
movl -0x8c(%rbp), %eax
movl %eax, %ecx
addl $-0x1, %ecx
movl %ecx, -0x8c(%rbp)
cmpl $0x0, %eax
jne 0xf9b3
jmp 0xfb67
movzwl -0x7a(%rbp), %eax
cmpq -0x88(%rbp), %rax
ja 0xf9d7
movq -0x88(%rbp), %rax
movzwl -0x7a(%rbp), %ecx
subq %rcx, %rax
movq %rax, -0xb0(%rbp)
jmp 0xf9f2
movq -0x88(%rbp), %rax
movzwl -0x7a(%rbp), %ecx
subq %rcx, %rax
movl -0x2c(%rbp), %ecx
addq %rcx, %rax
movq %rax, -0xb0(%rbp)
movq -0xb0(%rbp), %rax
movl %eax, -0x5c(%rbp)
cmpl $0x0, -0x5c(%rbp)
jbe 0xfb2c
movq -0x18(%rbp), %rax
movl -0x38(%rbp), %ecx
addq %rcx, %rax
movq %rax, -0x70(%rbp)
movq -0x18(%rbp), %rax
movl -0x38(%rbp), %ecx
subl -0x5c(%rbp), %ecx
movl %ecx, %ecx
addq %rcx, %rax
movq %rax, -0x78(%rbp)
cmpl $0x0, -0x34(%rbp)
je 0xfaa0
cmpl $0x0, -0x58(%rbp)
jne 0xfaa0
movq -0x10(%rbp), %rax
movq 0x8(%rax), %rax
movzwl -0x7a(%rbp), %ecx
cmpl $0x0, (%rax,%rcx,4)
jne 0xfaa0
movq -0x10(%rbp), %rax
movq 0x18(%rax), %rax
movzwl -0x7a(%rbp), %ecx
movw (%rax,%rcx,2), %ax
movw %ax, -0x8e(%rbp)
movzwl -0x8e(%rbp), %eax
movzwl -0x2e(%rbp), %ecx
cmpl %ecx, %eax
jle 0xfa76
movw -0x2e(%rbp), %ax
movw %ax, -0x8e(%rbp)
movzwl -0x8e(%rbp), %ecx
movq -0x78(%rbp), %rax
movslq %ecx, %rcx
addq %rcx, %rax
movq %rax, -0x78(%rbp)
movzwl -0x8e(%rbp), %ecx
movq -0x70(%rbp), %rax
movslq %ecx, %rcx
addq %rcx, %rax
movq %rax, -0x70(%rbp)
jmp 0xfaa2
movq -0x70(%rbp), %rcx
xorl %eax, %eax
cmpq -0x68(%rbp), %rcx
movb %al, -0xb1(%rbp)
je 0xfacd
movq -0x78(%rbp), %rax
movzbl (%rax), %eax
movq -0x70(%rbp), %rcx
movzbl (%rcx), %ecx
cmpl %ecx, %eax
sete %al
movb %al, -0xb1(%rbp)
movb -0xb1(%rbp), %al
testb $0x1, %al
jne 0xfad9
jmp 0xfaf3
movq -0x78(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x78(%rbp)
movq -0x70(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x70(%rbp)
jmp 0xfaa2
movq -0x70(%rbp), %rax
movq -0x18(%rbp), %rcx
movl -0x38(%rbp), %edx
addq %rdx, %rcx
subq %rcx, %rax
movl %eax, -0x60(%rbp)
movl -0x60(%rbp), %eax
cmpl -0x48(%rbp), %eax
jbe 0xfb2a
movl -0x60(%rbp), %eax
movl %eax, -0x48(%rbp)
movl -0x5c(%rbp), %eax
movl %eax, -0x44(%rbp)
movl -0x60(%rbp), %eax
cmpq $0x102, %rax            # imm = 0x102
jne 0xfb28
jmp 0xfb67
jmp 0xfb2a
jmp 0xfb2c
movzwl -0x7a(%rbp), %eax
movq -0x10(%rbp), %rcx
movq 0x10(%rcx), %rcx
movzwl -0x7a(%rbp), %edx
movzwl (%rcx,%rdx,2), %ecx
cmpl %ecx, %eax
jne 0xfb46
jmp 0xfb67
movw -0x7a(%rbp), %ax
movw %ax, -0x7c(%rbp)
movq -0x10(%rbp), %rax
movq 0x10(%rax), %rax
movzwl -0x7a(%rbp), %ecx
movw (%rax,%rcx,2), %ax
movw %ax, -0x7a(%rbp)
jmp 0xf942
jmp 0xfb69
cmpl $0x0, -0x4c(%rbp)
jne 0xfb98
cmpl $0x3, -0x48(%rbp)
jb 0xfb98
movl -0x48(%rbp), %eax
cmpq $0x102, %rax            # imm = 0x102
jae 0xfb98
movl $0x1, -0x4c(%rbp)
movl -0x48(%rbp), %eax
movl %eax, -0x50(%rbp)
movl -0x44(%rbp), %eax
movl %eax, -0x54(%rbp)
jmp 0xfdb7
cmpl $0x0, -0x4c(%rbp)
je 0xfc18
movl $0x0, -0x4c(%rbp)
cmpl $0x0, -0x38(%rbp)
jne 0xfbb7
movl $0x51, -0x40(%rbp)
jmp 0xfdc5
movl -0x48(%rbp), %eax
movl -0x50(%rbp), %ecx
addl $0x1, %ecx
cmpl %ecx, %eax
jbe 0xfbf0
movq -0x8(%rbp), %rdi
movq -0x18(%rbp), %rax
movl -0x38(%rbp), %ecx
subl $0x1, %ecx
movl %ecx, %ecx
movzbl (%rax,%rcx), %esi
callq 0xced0
cmpl $0x0, %eax
jne 0xfbee
movl $0x53, -0x40(%rbp)
jmp 0xfdc5
jmp 0xfc16
movl -0x50(%rbp), %eax
movl %eax, -0x48(%rbp)
movl -0x54(%rbp), %eax
movl %eax, -0x44(%rbp)
movq -0x10(%rbp), %rax
movq (%rax), %rax
movl -0x58(%rbp), %ecx
movl $0xffffffff, (%rax,%rcx,4) # imm = 0xFFFFFFFF
movl -0x38(%rbp), %eax
addl $-0x1, %eax
movl %eax, -0x38(%rbp)
jmp 0xfc18
cmpl $0x3, -0x48(%rbp)
jb 0xfc32
movl -0x44(%rbp), %eax
cmpl -0x2c(%rbp), %eax
jbe 0xfc32
movl $0x56, -0x40(%rbp)
jmp 0xfdc5
cmpl $0x3, -0x48(%rbp)
jae 0xfc62
movq -0x8(%rbp), %rdi
movq -0x18(%rbp), %rax
movl -0x38(%rbp), %ecx
movzbl (%rax,%rcx), %esi
callq 0xced0
cmpl $0x0, %eax
jne 0xfc5d
movl $0x53, -0x40(%rbp)
jmp 0xfdc5
jmp 0xfdb5
cmpl $0x3, -0x48(%rbp)
jne 0xfcf9
cmpl $0x800, -0x44(%rbp)     # imm = 0x800
jbe 0xfcf9
movq -0x8(%rbp), %rdi
movq -0x18(%rbp), %rax
movl -0x38(%rbp), %ecx
addl $0x0, %ecx
movl %ecx, %ecx
movzbl (%rax,%rcx), %esi
callq 0xced0
cmpl $0x0, %eax
jne 0xfca3
movl $0x53, -0x40(%rbp)
jmp 0xfdc5
movq -0x8(%rbp), %rdi
movq -0x18(%rbp), %rax
movl -0x38(%rbp), %ecx
addl $0x1, %ecx
movl %ecx, %ecx
movzbl (%rax,%rcx), %esi
callq 0xced0
cmpl $0x0, %eax
jne 0xfccd
movl $0x53, -0x40(%rbp)
jmp 0xfdc5
movq -0x8(%rbp), %rdi
movq -0x18(%rbp), %rax
movl -0x38(%rbp), %ecx
addl $0x2, %ecx
movl %ecx, %ecx
movzbl (%rax,%rcx), %esi
callq 0xced0
cmpl $0x0, %eax
jne 0xfcf7
movl $0x53, -0x40(%rbp)
jmp 0xfdc5
jmp 0xfd0c
movq -0x8(%rbp), %rdi
movl -0x48(%rbp), %eax
movl %eax, %esi
movl -0x44(%rbp), %eax
movl %eax, %edx
callq 0x101d0
movl $0x1, -0x3c(%rbp)
movl -0x3c(%rbp), %eax
cmpl -0x48(%rbp), %eax
jae 0xfdb3
movl -0x38(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x38(%rbp)
movq -0x18(%rbp), %rdi
movq -0x28(%rbp), %rsi
movl -0x38(%rbp), %eax
movl %eax, %edx
callq 0x10010
movl %eax, -0x58(%rbp)
movq -0x10(%rbp), %rdi
movl -0x38(%rbp), %eax
movl %eax, %esi
movl -0x58(%rbp), %edx
movl -0x2c(%rbp), %ecx
callq 0x100b0
cmpl $0x0, -0x34(%rbp)
je 0xfda3
cmpl $0x0, -0x58(%rbp)
jne 0xfda3
movq -0x18(%rbp), %rdi
movq -0x28(%rbp), %rsi
movl -0x38(%rbp), %eax
movl %eax, %edx
callq 0x10130
movw %ax, -0xb4(%rbp)
movq -0x10(%rbp), %rax
movq 0x18(%rax), %rax
movq %rax, -0xc0(%rbp)
movl -0x38(%rbp), %eax
xorl %edx, %edx
divl -0x2c(%rbp)
movq -0xc0(%rbp), %rax
movl %edx, %ecx
movw -0xb4(%rbp), %dx
movl %ecx, %ecx
movw %dx, (%rax,%rcx,2)
jmp 0xfda5
movl -0x3c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x3c(%rbp)
jmp 0xfd13
jmp 0xfdb5
jmp 0xfdb7
movl -0x38(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x38(%rbp)
jmp 0xf7f8
jmp 0xfdc7
movl -0x40(%rbp), %eax
addq $0xc0, %rsp
popq %rbp
retq
nopw %cs:(%rax,%rax)