// File: sbingner[P]kerneldec/lzfse/src/lzfse_encode.c
// Function: lzfse_encode_buffer_with_scratch
size_t lzfse_encode_buffer_with_scratch(uint8_t *__restrict dst_buffer, 
                       size_t dst_size, const uint8_t *__restrict src_buffer,
                       size_t src_size, void *__restrict scratch_buffer) {
  const size_t original_size = src_size;

  // If input is really really small, go directly to uncompressed buffer
  // (because LZVN will refuse to encode it, and we will report a failure)
  if (src_size < LZVN_ENCODE_MIN_SRC_SIZE)
    goto try_uncompressed;

  // If input is too small, try encoding with LZVN
  if (src_size < LZFSE_ENCODE_LZVN_THRESHOLD) {
    // need header + end-of-stream marker
    size_t extra_size = 4 + sizeof(lzvn_compressed_block_header);
    if (dst_size <= extra_size)
      goto try_uncompressed; // DST is really too small, give up

    size_t sz = lzvn_encode_buffer(
        dst_buffer + sizeof(lzvn_compressed_block_header),
        dst_size - extra_size, src_buffer, src_size, scratch_buffer);
    if (sz == 0 || sz >= src_size)
      goto try_uncompressed; // failed, or no compression, fall back to
                             // uncompressed block

    // If we could encode, setup header and end-of-stream marker (we left room
    // for them, no need to test)
    lzvn_compressed_block_header header;
    header.magic = LZFSE_COMPRESSEDLZVN_BLOCK_MAGIC;
    header.n_raw_bytes = (uint32_t)src_size;
    header.n_payload_bytes = (uint32_t)sz;
    memcpy(dst_buffer, &header, sizeof(header));
    store4(dst_buffer + sizeof(lzvn_compressed_block_header) + sz,
           LZFSE_ENDOFSTREAM_BLOCK_MAGIC);

    return sz + extra_size;
  }

  // Try encoding with LZFSE
  {
    lzfse_encoder_state *state = scratch_buffer;
    memset(state, 0x00, sizeof *state);
    if (lzfse_encode_init(state) != LZFSE_STATUS_OK)
      goto try_uncompressed;
    state->dst = dst_buffer;
    state->dst_begin = dst_buffer;
    state->dst_end = &dst_buffer[dst_size];
    state->src = src_buffer;
    state->src_encode_i = 0;

    if (src_size >= 0xffffffffU) {
      //  lzfse only uses 32 bits for offsets internally, so if the input
      //  buffer is really huge, we need to process it in smaller chunks.
      //  Note that we switch over to this path for sizes much smaller
      //  2GB because it's actually faster to change algorithms well before
      //  it's necessary for correctness.
      //  The first chunk, we just process normally.
      const lzfse_offset encoder_block_size = 262144;
      state->src_end = encoder_block_size;
      if (lzfse_encode_base(state) != LZFSE_STATUS_OK)
        goto try_uncompressed;
      src_size -= encoder_block_size;
      while (src_size >= encoder_block_size) {
        //  All subsequent chunks require a translation to keep the offsets
        //  from getting too big.  Note that we are always going from
        //  encoder_block_size up to 2*encoder_block_size so that the
        //  offsets remain positive (as opposed to resetting to zero and
        //  having negative offsets).
        state->src_end = 2 * encoder_block_size;
        if (lzfse_encode_base(state) != LZFSE_STATUS_OK)
          goto try_uncompressed;
        lzfse_encode_translate(state, encoder_block_size);
        src_size -= encoder_block_size;
      }
      //  Set the end for the final chunk.
      state->src_end = encoder_block_size + (lzfse_offset)src_size;
    }
    //  If the source buffer is small enough to use 32-bit offsets, we simply
    //  encode the whole thing in a single chunk.
    else
      state->src_end = (lzfse_offset)src_size;
    //  This is either the trailing chunk (if the source file is huge), or
    //  the whole source file.
    if (lzfse_encode_base(state) != LZFSE_STATUS_OK)
      goto try_uncompressed;
    if (lzfse_encode_finish(state) != LZFSE_STATUS_OK)
      goto try_uncompressed;
    //  No error occured, return compressed size.
    return state->dst - dst_buffer;
  }

try_uncompressed:
  //  Compression failed for some reason.  If we can fit the data into the
  //  output buffer uncompressed, go ahead and do that instead.
  if (original_size + 12 <= dst_size && original_size < INT32_MAX) {
    uncompressed_block_header header = {.magic = LZFSE_UNCOMPRESSED_BLOCK_MAGIC,
                                        .n_raw_bytes = (uint32_t)src_size};
    uint8_t *dst_end = dst_buffer;
    memcpy(dst_end, &header, sizeof header);
    dst_end += sizeof header;
    memcpy(dst_end, src_buffer, original_size);
    dst_end += original_size;
    store4(dst_end, LZFSE_ENDOFSTREAM_BLOCK_MAGIC);
    dst_end += 4;
    return dst_end - dst_buffer;
  }

  //  Otherwise, there's nothing we can do, so return zero.
  return 0;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0xa0, %rsp
movq %rdi, -0x30(%rbp)
movq %rsi, -0x38(%rbp)
movq %rdx, -0x40(%rbp)
movq %rcx, -0x48(%rbp)
movq %r8, -0x50(%rbp)
movq -0x48(%rbp), %rax
movq %rax, -0x58(%rbp)
cmpq $0x8, -0x48(%rbp)
jae 0x37c3
jmp 0x39c8
cmpq $0x1000, -0x48(%rbp)    # imm = 0x1000
jae 0x387a
movq $0x10, -0x60(%rbp)
movq -0x38(%rbp), %rax
cmpq -0x60(%rbp), %rax
ja 0x37e8
jmp 0x39c8
movq -0x30(%rbp), %rdi
addq $0xc, %rdi
movq -0x38(%rbp), %rsi
subq -0x60(%rbp), %rsi
movq -0x40(%rbp), %rdx
movq -0x48(%rbp), %rcx
movq -0x50(%rbp), %r8
callq 0xb290
movq %rax, -0x68(%rbp)
cmpq $0x0, -0x68(%rbp)
je 0x381e
movq -0x68(%rbp), %rax
cmpq -0x48(%rbp), %rax
jb 0x3823
jmp 0x39c8
movl $0x6e787662, -0x74(%rbp) # imm = 0x6E787662
movq -0x48(%rbp), %rax
movl %eax, -0x70(%rbp)
movq -0x68(%rbp), %rax
movl %eax, -0x6c(%rbp)
movq -0x30(%rbp), %rax
movq -0x74(%rbp), %rcx
movq %rcx, (%rax)
movl -0x6c(%rbp), %ecx
movl %ecx, 0x8(%rax)
movq -0x30(%rbp), %rax
addq $0xc, %rax
addq -0x68(%rbp), %rax
movq %rax, -0x8(%rbp)
movl $0x24787662, -0xc(%rbp) # imm = 0x24787662
movq -0x8(%rbp), %rax
movl -0xc(%rbp), %ecx
movl %ecx, (%rax)
movq -0x68(%rbp), %rax
addq -0x60(%rbp), %rax
movq %rax, -0x28(%rbp)
jmp 0x3a99
movq -0x50(%rbp), %rax
movq %rax, -0x80(%rbp)
movq -0x80(%rbp), %rdi
xorl %esi, %esi
movl $0xa7160, %edx          # imm = 0xA7160
callq 0x3080
movq -0x80(%rbp), %rdi
callq 0x4300
cmpl $0x0, %eax
je 0x38a5
jmp 0x39c8
movq -0x30(%rbp), %rcx
movq -0x80(%rbp), %rax
movq %rcx, 0x28(%rax)
movq -0x30(%rbp), %rcx
movq -0x80(%rbp), %rax
movq %rcx, 0x30(%rax)
movq -0x30(%rbp), %rcx
addq -0x38(%rbp), %rcx
movq -0x80(%rbp), %rax
movq %rcx, 0x38(%rax)
movq -0x40(%rbp), %rcx
movq -0x80(%rbp), %rax
movq %rcx, (%rax)
movq -0x80(%rbp), %rax
movq $0x0, 0x18(%rax)
movl $0xffffffff, %eax       # imm = 0xFFFFFFFF
cmpq %rax, -0x48(%rbp)
jb 0x3984
movq $0x40000, -0x88(%rbp) # imm = 0x40000
movq -0x80(%rbp), %rax
movq $0x40000, 0x8(%rax)     # imm = 0x40000
movq -0x80(%rbp), %rdi
callq 0x4550
cmpl $0x0, %eax
je 0x391d
jmp 0x39c8
movq -0x48(%rbp), %rax
subq $0x40000, %rax          # imm = 0x40000
movq %rax, -0x48(%rbp)
cmpq $0x40000, -0x48(%rbp)   # imm = 0x40000
jb 0x396f
movq -0x80(%rbp), %rax
movq $0x80000, 0x8(%rax)     # imm = 0x80000
movq -0x80(%rbp), %rdi
callq 0x4550
cmpl $0x0, %eax
je 0x3951
jmp 0x39c8
movq -0x80(%rbp), %rdi
movl $0x40000, %esi          # imm = 0x40000
callq 0x43d0
movq -0x48(%rbp), %rax
subq $0x40000, %rax          # imm = 0x40000
movq %rax, -0x48(%rbp)
jmp 0x392b
movq -0x48(%rbp), %rcx
addq $0x40000, %rcx          # imm = 0x40000
movq -0x80(%rbp), %rax
movq %rcx, 0x8(%rax)
jmp 0x3990
movq -0x48(%rbp), %rcx
movq -0x80(%rbp), %rax
movq %rcx, 0x8(%rax)
movq -0x80(%rbp), %rdi
callq 0x4550
cmpl $0x0, %eax
je 0x39a0
jmp 0x39c8
movq -0x80(%rbp), %rdi
callq 0x4cb0
cmpl $0x0, %eax
je 0x39b0
jmp 0x39c8
movq -0x80(%rbp), %rax
movq 0x28(%rax), %rax
movq -0x30(%rbp), %rcx
subq %rcx, %rax
movq %rax, -0x28(%rbp)
jmp 0x3a99
movq -0x58(%rbp), %rax
addq $0xc, %rax
cmpq -0x38(%rbp), %rax
ja 0x3a91
cmpq $0x7fffffff, -0x58(%rbp) # imm = 0x7FFFFFFF
jae 0x3a91
movl $0x2d787662, -0x90(%rbp) # imm = 0x2D787662
movq -0x48(%rbp), %rax
movl %eax, -0x8c(%rbp)
movq -0x30(%rbp), %rax
movq %rax, -0x98(%rbp)
movq -0x98(%rbp), %rax
movq -0x90(%rbp), %rcx
movq %rcx, (%rax)
movq -0x98(%rbp), %rax
addq $0x8, %rax
movq %rax, -0x98(%rbp)
movq -0x98(%rbp), %rdi
movq -0x40(%rbp), %rsi
movq -0x58(%rbp), %rdx
callq 0x30c0
movq -0x58(%rbp), %rax
addq -0x98(%rbp), %rax
movq %rax, -0x98(%rbp)
movq -0x98(%rbp), %rax
movq %rax, -0x18(%rbp)
movl $0x24787662, -0x1c(%rbp) # imm = 0x24787662
movq -0x18(%rbp), %rax
movl -0x1c(%rbp), %ecx
movl %ecx, (%rax)
movq -0x98(%rbp), %rax
addq $0x4, %rax
movq %rax, -0x98(%rbp)
movq -0x98(%rbp), %rax
movq -0x30(%rbp), %rcx
subq %rcx, %rax
movq %rax, -0x28(%rbp)
jmp 0x3a99
movq $0x0, -0x28(%rbp)
movq -0x28(%rbp), %rax
addq $0xa0, %rsp
popq %rbp
retq
nopw %cs:(%rax,%rax)