// File: sbingner[P]kerneldec/lzfse/src/lzvn_encode_base.c
// Function: lzvn_encode
void lzvn_encode(lzvn_encoder_state *state) {
  const lzvn_match_info NO_MATCH = {0};

  for (; state->src_current < state->src_current_end; state->src_current++) {
    // Get 4 bytes at src_current
    uint32_t vi = load4(state->src + state->src_current);

    // Compute new hash H at position I, and push value into position table
    int h = hash3i(vi); // index of first entry

    // Read table entries for H
    lzvn_encode_entry_type e = state->table[h];

    // Update entry with index=current and value=vi
    lzvn_encode_entry_type updated_e; // rotate values, so we will replace the oldest
    updated_e.indices[0] = offset_to_s32(state->src_current);
    updated_e.indices[1] = e.indices[0];
    updated_e.indices[2] = e.indices[1];
    updated_e.indices[3] = e.indices[2];
    updated_e.values[0] = vi;
    updated_e.values[1] = e.values[0];
    updated_e.values[2] = e.values[1];
    updated_e.values[3] = e.values[2];

    // Do not check matches if still in previously emitted match
    if (state->src_current < state->src_literal)
      goto after_emit;

// Update best with candidate if better
#define UPDATE(best, candidate)                                                \
  do {                                                                         \
    if (candidate.K > best.K ||                                                \
        ((candidate.K == best.K) && (candidate.m_end > best.m_end + 1))) {     \
      best = candidate;                                                        \
    }                                                                          \
  } while (0)
// Check candidate. Keep if better.
#define CHECK_CANDIDATE(ik, nk)                                                \
  do {                                                                         \
    lzvn_match_info m1;                                                              \
    if (lzvn_find_matchN(state->src, state->src_begin, state->src_end,               \
                   state->src_literal, ik, state->src_current, nk, &m1)) {     \
      UPDATE(incoming, m1);                                                    \
    }                                                                          \
  } while (0)
// Emit match M. Return if we don't have enough space in the destination buffer
#define EMIT_MATCH(m)                                                          \
  do {                                                                         \
    if (lzvn_emit_match(state, m) == 0)                                              \
      return;                                                                  \
  } while (0)
// Emit literal of length L. Return if we don't have enough space in the
// destination buffer
#define EMIT_LITERAL(l)                                                        \
  do {                                                                         \
    if (lzvn_emit_literal(state, l) == 0)                                            \
      return;                                                                  \
  } while (0)

    lzvn_match_info incoming = NO_MATCH;

    // Check candidates in order (closest first)
    uint32_t diffs[4];
    for (int k = 0; k < 4; k++)
      diffs[k] = e.values[k] ^ vi; // XOR, 0 if equal
    lzvn_offset ik;                // index
    lzvn_offset nk;                // match byte count

    // The values stored in e.xyzw are 32-bit signed indices, extended to signed
    // type lzvn_offset
    ik = offset_from_s32(e.indices[0]);
    nk = trailing_zero_bytes(diffs[0]);
    CHECK_CANDIDATE(ik, nk);
    ik = offset_from_s32(e.indices[1]);
    nk = trailing_zero_bytes(diffs[1]);
    CHECK_CANDIDATE(ik, nk);
    ik = offset_from_s32(e.indices[2]);
    nk = trailing_zero_bytes(diffs[2]);
    CHECK_CANDIDATE(ik, nk);
    ik = offset_from_s32(e.indices[3]);
    nk = trailing_zero_bytes(diffs[3]);
    CHECK_CANDIDATE(ik, nk);

    // Check candidate at previous distance
    if (state->d_prev != 0) {
      lzvn_match_info m1;
      if (lzvn_find_match(state->src, state->src_begin, state->src_end,
                    state->src_literal, state->src_current - state->d_prev,
                    state->src_current, &m1)) {
        m1.K = m1.M - 1; // fix K for D_prev
        UPDATE(incoming, m1);
      }
    }

    // Here we have the best candidate in incoming, may be NO_MATCH

    // If no incoming match, and literal backlog becomes too high, emit pending
    // match, or literals if there is no pending match
    if (incoming.M == 0) {
      if (state->src_current - state->src_literal >=
          LZVN_ENCODE_MAX_LITERAL_BACKLOG) // at this point, we always have
                                           // current >= literal
      {
        if (state->pending.M != 0) {
          EMIT_MATCH(state->pending);
          state->pending = NO_MATCH;
        } else {
          EMIT_LITERAL(271); // emit long literal (271 is the longest literal size we allow)
        }
      }
      goto after_emit;
    }

    if (state->pending.M == 0) {
      // NOTE. Here, we can also emit incoming right away. It will make the
      // encoder 1.5x faster, at a cost of ~10% lower compression ratio:
      // EMIT_MATCH(incoming);
      // state->pending = NO_MATCH;

      // No pending match, emit nothing, keep incoming
      state->pending = incoming;
    } else {
      // Here we have both incoming and pending
      if (state->pending.m_end <= incoming.m_begin) {
        // No overlap: emit pending, keep incoming
        EMIT_MATCH(state->pending);
        state->pending = incoming;
      } else {
        // If pending is better, emit pending and discard incoming.
        // Otherwise, emit incoming and discard pending.
        if (incoming.K > state->pending.K)
          state->pending = incoming;
        EMIT_MATCH(state->pending);
        state->pending = NO_MATCH;
      }
    }

  after_emit:

    // We commit state changes only after we tried to emit instructions, so we
    // can restart in the same state in case dst was full and we quit the loop.
    state->table[h] = updated_e;

  } // i loop

  // Do not emit pending match here. We do it only at the end of stream.
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x1d0, %rsp            # imm = 0x1D0
movq %rdi, -0x18(%rbp)
leaq -0x40(%rbp), %rdi
xorl %esi, %esi
movl $0x28, %edx
callq 0x3080
movq -0x18(%rbp), %rax
movq 0x18(%rax), %rax
movq -0x18(%rbp), %rcx
cmpq 0x20(%rcx), %rax
jge 0xad26
movq -0x18(%rbp), %rax
movq (%rax), %rax
movq -0x18(%rbp), %rcx
addq 0x18(%rcx), %rax
movq %rax, -0x8(%rbp)
movq -0x8(%rbp), %rax
movl (%rax), %eax
movl %eax, -0xc(%rbp)
movl -0xc(%rbp), %eax
movl %eax, -0x44(%rbp)
movl -0x44(%rbp), %edi
callq 0xad30
movl %eax, -0x48(%rbp)
movq -0x18(%rbp), %rax
movq 0x78(%rax), %rax
movslq -0x48(%rbp), %rcx
shlq $0x5, %rcx
addq %rcx, %rax
movq (%rax), %rcx
movq %rcx, -0x68(%rbp)
movq 0x8(%rax), %rcx
movq %rcx, -0x60(%rbp)
movq 0x10(%rax), %rcx
movq %rcx, -0x58(%rbp)
movq 0x18(%rax), %rax
movq %rax, -0x50(%rbp)
movq -0x18(%rbp), %rax
movq 0x18(%rax), %rdi
callq 0xad60
movl %eax, -0x88(%rbp)
movl -0x68(%rbp), %eax
movl %eax, -0x84(%rbp)
movl -0x64(%rbp), %eax
movl %eax, -0x80(%rbp)
movl -0x60(%rbp), %eax
movl %eax, -0x7c(%rbp)
movl -0x44(%rbp), %eax
movl %eax, -0x78(%rbp)
movl -0x58(%rbp), %eax
movl %eax, -0x74(%rbp)
movl -0x54(%rbp), %eax
movl %eax, -0x70(%rbp)
movl -0x50(%rbp), %eax
movl %eax, -0x6c(%rbp)
movq -0x18(%rbp), %rax
movq 0x18(%rax), %rax
movq -0x18(%rbp), %rcx
cmpq 0x28(%rcx), %rax
jge 0xa6fb
jmp 0xacdc
leaq -0xb0(%rbp), %rdi
xorl %esi, %esi
movl $0x28, %edx
callq 0x3080
movl $0x0, -0xc4(%rbp)
cmpl $0x4, -0xc4(%rbp)
jge 0xa74e
movslq -0xc4(%rbp), %rax
movl -0x58(%rbp,%rax,4), %ecx
xorl -0x44(%rbp), %ecx
movslq -0xc4(%rbp), %rax
movl %ecx, -0xc0(%rbp,%rax,4)
movl -0xc4(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xc4(%rbp)
jmp 0xa718
movl -0x68(%rbp), %edi
callq 0xad70
movq %rax, -0xd0(%rbp)
movl -0xc0(%rbp), %edi
callq 0xad80
movq %rax, -0xd8(%rbp)
movq -0x18(%rbp), %rax
movq (%rax), %rdi
movq -0x18(%rbp), %rax
movq 0x8(%rax), %rsi
movq -0x18(%rbp), %rax
movq 0x10(%rax), %rdx
movq -0x18(%rbp), %rax
movq 0x28(%rax), %rcx
movq -0xd0(%rbp), %r8
movq -0x18(%rbp), %rax
movq 0x18(%rax), %r9
movq -0xd8(%rbp), %r10
leaq -0x100(%rbp), %rax
movq %r10, (%rsp)
movq %rax, 0x8(%rsp)
callq 0xadb0
cmpl $0x0, %eax
je 0xa813
jmp 0xa7c0
movq -0xe0(%rbp), %rax
cmpq -0x90(%rbp), %rax
jg 0xa7f7
movq -0xe0(%rbp), %rax
cmpq -0x90(%rbp), %rax
jne 0xa80f
movq -0xf8(%rbp), %rax
movq -0xa8(%rbp), %rcx
addq $0x1, %rcx
cmpq %rcx, %rax
jle 0xa80f
leaq -0xb0(%rbp), %rdi
leaq -0x100(%rbp), %rsi
movl $0x28, %edx
callq 0x30c0
jmp 0xa811
jmp 0xa813
jmp 0xa815
movl -0x64(%rbp), %edi
callq 0xad70
movq %rax, -0xd0(%rbp)
movl -0xbc(%rbp), %edi
callq 0xad80
movq %rax, -0xd8(%rbp)
movq -0x18(%rbp), %rax
movq (%rax), %rdi
movq -0x18(%rbp), %rax
movq 0x8(%rax), %rsi
movq -0x18(%rbp), %rax
movq 0x10(%rax), %rdx
movq -0x18(%rbp), %rax
movq 0x28(%rax), %rcx
movq -0xd0(%rbp), %r8
movq -0x18(%rbp), %rax
movq 0x18(%rax), %r9
movq -0xd8(%rbp), %r10
leaq -0x128(%rbp), %rax
movq %r10, (%rsp)
movq %rax, 0x8(%rsp)
callq 0xadb0
cmpl $0x0, %eax
je 0xa8da
jmp 0xa887
movq -0x108(%rbp), %rax
cmpq -0x90(%rbp), %rax
jg 0xa8be
movq -0x108(%rbp), %rax
cmpq -0x90(%rbp), %rax
jne 0xa8d6
movq -0x120(%rbp), %rax
movq -0xa8(%rbp), %rcx
addq $0x1, %rcx
cmpq %rcx, %rax
jle 0xa8d6
leaq -0xb0(%rbp), %rdi
leaq -0x128(%rbp), %rsi
movl $0x28, %edx
callq 0x30c0
jmp 0xa8d8
jmp 0xa8da
jmp 0xa8dc
movl -0x60(%rbp), %edi
callq 0xad70
movq %rax, -0xd0(%rbp)
movl -0xb8(%rbp), %edi
callq 0xad80
movq %rax, -0xd8(%rbp)
movq -0x18(%rbp), %rax
movq (%rax), %rdi
movq -0x18(%rbp), %rax
movq 0x8(%rax), %rsi
movq -0x18(%rbp), %rax
movq 0x10(%rax), %rdx
movq -0x18(%rbp), %rax
movq 0x28(%rax), %rcx
movq -0xd0(%rbp), %r8
movq -0x18(%rbp), %rax
movq 0x18(%rax), %r9
movq -0xd8(%rbp), %r10
leaq -0x150(%rbp), %rax
movq %r10, (%rsp)
movq %rax, 0x8(%rsp)
callq 0xadb0
cmpl $0x0, %eax
je 0xa9a1
jmp 0xa94e
movq -0x130(%rbp), %rax
cmpq -0x90(%rbp), %rax
jg 0xa985
movq -0x130(%rbp), %rax
cmpq -0x90(%rbp), %rax
jne 0xa99d
movq -0x148(%rbp), %rax
movq -0xa8(%rbp), %rcx
addq $0x1, %rcx
cmpq %rcx, %rax
jle 0xa99d
leaq -0xb0(%rbp), %rdi
leaq -0x150(%rbp), %rsi
movl $0x28, %edx
callq 0x30c0
jmp 0xa99f
jmp 0xa9a1
jmp 0xa9a3
movl -0x5c(%rbp), %edi
callq 0xad70
movq %rax, -0xd0(%rbp)
movl -0xb4(%rbp), %edi
callq 0xad80
movq %rax, -0xd8(%rbp)
movq -0x18(%rbp), %rax
movq (%rax), %rdi
movq -0x18(%rbp), %rax
movq 0x8(%rax), %rsi
movq -0x18(%rbp), %rax
movq 0x10(%rax), %rdx
movq -0x18(%rbp), %rax
movq 0x28(%rax), %rcx
movq -0xd0(%rbp), %r8
movq -0x18(%rbp), %rax
movq 0x18(%rax), %r9
movq -0xd8(%rbp), %r10
leaq -0x178(%rbp), %rax
movq %r10, (%rsp)
movq %rax, 0x8(%rsp)
callq 0xadb0
cmpl $0x0, %eax
je 0xaa68
jmp 0xaa15
movq -0x158(%rbp), %rax
cmpq -0x90(%rbp), %rax
jg 0xaa4c
movq -0x158(%rbp), %rax
cmpq -0x90(%rbp), %rax
jne 0xaa64
movq -0x170(%rbp), %rax
movq -0xa8(%rbp), %rcx
addq $0x1, %rcx
cmpq %rcx, %rax
jle 0xaa64
leaq -0xb0(%rbp), %rdi
leaq -0x178(%rbp), %rsi
movl $0x28, %edx
callq 0x30c0
jmp 0xaa66
jmp 0xaa68
jmp 0xaa6a
movq -0x18(%rbp), %rax
cmpq $0x0, 0x70(%rax)
je 0xab2c
movq -0x18(%rbp), %rax
movq (%rax), %rdi
movq -0x18(%rbp), %rax
movq 0x8(%rax), %rsi
movq -0x18(%rbp), %rax
movq 0x10(%rax), %rdx
movq -0x18(%rbp), %rax
movq 0x28(%rax), %rcx
movq -0x18(%rbp), %rax
movq 0x18(%rax), %r8
movq -0x18(%rbp), %rax
subq 0x70(%rax), %r8
movq -0x18(%rbp), %rax
movq 0x18(%rax), %r9
leaq -0x1a0(%rbp), %rax
movq %rax, (%rsp)
callq 0xaf50
cmpl $0x0, %eax
je 0xab2a
movq -0x190(%rbp), %rax
subq $0x1, %rax
movq %rax, -0x180(%rbp)
movq -0x180(%rbp), %rax
cmpq -0x90(%rbp), %rax
jg 0xab0e
movq -0x180(%rbp), %rax
cmpq -0x90(%rbp), %rax
jne 0xab26
movq -0x198(%rbp), %rax
movq -0xa8(%rbp), %rcx
addq $0x1, %rcx
cmpq %rcx, %rax
jle 0xab26
leaq -0xb0(%rbp), %rdi
leaq -0x1a0(%rbp), %rsi
movl $0x28, %edx
callq 0x30c0
jmp 0xab28
jmp 0xab2a
jmp 0xab2c
cmpq $0x0, -0xa0(%rbp)
jne 0xabd4
movq -0x18(%rbp), %rax
movq 0x18(%rax), %rax
movq -0x18(%rbp), %rcx
subq 0x28(%rcx), %rax
cmpq $0x190, %rax            # imm = 0x190
jl 0xabcf
movq -0x18(%rbp), %rax
cmpq $0x0, 0x58(%rax)
je 0xabae
jmp 0xab5f
movq -0x18(%rbp), %rdi
movq -0x18(%rbp), %rcx
addq $0x48, %rcx
movq 0x20(%rcx), %rdx
movq %rsp, %rax
movq %rdx, 0x20(%rax)
movups (%rcx), %xmm0
movups 0x10(%rcx), %xmm1
movups %xmm1, 0x10(%rax)
movups %xmm0, (%rax)
callq 0xb100
cmpq $0x0, %rax
jne 0xab94
jmp 0xad26
jmp 0xab96
movq -0x18(%rbp), %rdi
addq $0x48, %rdi
leaq -0x40(%rbp), %rsi
movl $0x28, %edx
callq 0x30c0
jmp 0xabcd
jmp 0xabb0
movq -0x18(%rbp), %rdi
movl $0x10f, %esi            # imm = 0x10F
callq 0xb1e0
cmpq $0x0, %rax
jne 0xabc9
jmp 0xad26
jmp 0xabcb
jmp 0xabcd
jmp 0xabcf
jmp 0xacdc
movq -0x18(%rbp), %rax
cmpq $0x0, 0x58(%rax)
jne 0xabfd
movq -0x18(%rbp), %rdi
addq $0x48, %rdi
leaq -0xb0(%rbp), %rsi
movl $0x28, %edx
callq 0x30c0
jmp 0xacda
movq -0x18(%rbp), %rax
movq 0x50(%rax), %rax
cmpq -0xb0(%rbp), %rax
jg 0xac62
jmp 0xac10
movq -0x18(%rbp), %rdi
movq -0x18(%rbp), %rcx
addq $0x48, %rcx
movq 0x20(%rcx), %rdx
movq %rsp, %rax
movq %rdx, 0x20(%rax)
movups (%rcx), %xmm0
movups 0x10(%rcx), %xmm1
movups %xmm1, 0x10(%rax)
movups %xmm0, (%rax)
callq 0xb100
cmpq $0x0, %rax
jne 0xac45
jmp 0xad26
jmp 0xac47
movq -0x18(%rbp), %rdi
addq $0x48, %rdi
leaq -0xb0(%rbp), %rsi
movl $0x28, %edx
callq 0x30c0
jmp 0xacd8
movq -0x90(%rbp), %rax
movq -0x18(%rbp), %rcx
cmpq 0x68(%rcx), %rax
jle 0xac8c
movq -0x18(%rbp), %rdi
addq $0x48, %rdi
leaq -0xb0(%rbp), %rsi
movl $0x28, %edx
callq 0x30c0
jmp 0xac8e
movq -0x18(%rbp), %rdi
movq -0x18(%rbp), %rcx
addq $0x48, %rcx
movq 0x20(%rcx), %rdx
movq %rsp, %rax
movq %rdx, 0x20(%rax)
movups (%rcx), %xmm0
movups 0x10(%rcx), %xmm1
movups %xmm1, 0x10(%rax)
movups %xmm0, (%rax)
callq 0xb100
cmpq $0x0, %rax
jne 0xacc0
jmp 0xad26
jmp 0xacc2
movq -0x18(%rbp), %rdi
addq $0x48, %rdi
leaq -0x40(%rbp), %rsi
movl $0x28, %edx
callq 0x30c0
jmp 0xacda
jmp 0xacdc
movq -0x18(%rbp), %rax
movq 0x78(%rax), %rax
movslq -0x48(%rbp), %rcx
shlq $0x5, %rcx
addq %rcx, %rax
movq -0x88(%rbp), %rcx
movq %rcx, (%rax)
movq -0x80(%rbp), %rcx
movq %rcx, 0x8(%rax)
movq -0x78(%rbp), %rcx
movq %rcx, 0x10(%rax)
movq -0x70(%rbp), %rcx
movq %rcx, 0x18(%rax)
movq -0x18(%rbp), %rax
movq 0x18(%rax), %rcx
addq $0x1, %rcx
movq %rcx, 0x18(%rax)
jmp 0xa62f
addq $0x1d0, %rsp            # imm = 0x1D0
popq %rbp
retq
nop