// File: /workspace/github_c_makefile_star2_O3/FH0[P]tun_direct/lwip-2.1.2/src/core/tcp_out.c
// Function: tcp_output
err_t
tcp_output(struct tcp_pcb *pcb)
{
  struct tcp_seg *seg, *useg;
  u32_t wnd, snd_nxt;
  err_t err;
  struct netif *netif;
#if TCP_CWND_DEBUG
  s16_t i = 0;
#endif /* TCP_CWND_DEBUG */

  LWIP_ASSERT_CORE_LOCKED();

  LWIP_ASSERT("tcp_output: invalid pcb", pcb != NULL);
  /* pcb->state LISTEN not allowed here */
  LWIP_ASSERT("don't call tcp_output for listen-pcbs",
              pcb->state != LISTEN);

  /* First, check if we are invoked by the TCP input processing
     code. If so, we do not output anything. Instead, we rely on the
     input processing code to call us when input processing is done
     with. */
  if (tcp_input_pcb == pcb) {
    return ERR_OK;
  }

  wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);

  seg = pcb->unsent;

  if (seg == NULL) {
    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n",
                                   (void *)pcb->unsent));
    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"TCPWNDSIZE_F
                                 ", cwnd %"TCPWNDSIZE_F", wnd %"U32_F
                                 ", seg == NULL, ack %"U32_F"\n",
                                 pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack));

    /* If the TF_ACK_NOW flag is set and the ->unsent queue is empty, construct
     * an empty ACK segment and send it. */
    if (pcb->flags & TF_ACK_NOW) {
      return tcp_send_empty_ack(pcb);
    }
    /* nothing to send: shortcut out of here */
    goto output_done;
  } else {
    LWIP_DEBUGF(TCP_CWND_DEBUG,
                ("tcp_output: snd_wnd %"TCPWNDSIZE_F", cwnd %"TCPWNDSIZE_F", wnd %"U32_F
                 ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n",
                 pcb->snd_wnd, pcb->cwnd, wnd,
                 lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
                 lwip_ntohl(seg->tcphdr->seqno), pcb->lastack));
  }

  netif = tcp_route(pcb, &pcb->local_ip, &pcb->remote_ip);
  if (netif == NULL) {
    return ERR_RTE;
  }

  /* If we don't have a local IP address, we get one from netif */
  if (ip_addr_isany(&pcb->local_ip)) {
    const ip_addr_t *local_ip = ip_netif_get_local_ip(netif, &pcb->remote_ip);
    if (local_ip == NULL) {
      return ERR_RTE;
    }
    ip_addr_copy(pcb->local_ip, *local_ip);
  }

  /* Handle the current segment not fitting within the window */
  if (lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd) {
    /* We need to start the persistent timer when the next unsent segment does not fit
     * within the remaining (could be 0) send window and RTO timer is not running (we
     * have no in-flight data). If window is still too small after persist timer fires,
     * then we split the segment. We don't consider the congestion window since a cwnd
     * smaller than 1 SMSS implies in-flight data
     */
    if (wnd == pcb->snd_wnd && pcb->unacked == NULL && pcb->persist_backoff == 0) {
      pcb->persist_cnt = 0;
      pcb->persist_backoff = 1;
      pcb->persist_probe = 0;
    }
    /* We need an ACK, but can't send data now, so send an empty ACK */
    if (pcb->flags & TF_ACK_NOW) {
      return tcp_send_empty_ack(pcb);
    }
    goto output_done;
  }
  /* Stop persist timer, above conditions are not active */
  pcb->persist_backoff = 0;

  /* useg should point to last segment on unacked queue */
  useg = pcb->unacked;
  if (useg != NULL) {
    for (; useg->next != NULL; useg = useg->next);
  }
  /* data available and window allows it to be sent? */
  while (seg != NULL &&
         lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
    LWIP_ASSERT("RST not expected here!",
                (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0);
    /* Stop sending if the nagle algorithm would prevent it
     * Don't stop:
     * - if tcp_write had a memory error before (prevent delayed ACK timeout) or
     * - if FIN was already enqueued for this PCB (SYN is always alone in a segment -
     *   either seg->next != NULL or pcb->unacked == NULL;
     *   RST is no sent using tcp_write/tcp_output.
     */
    if ((tcp_do_output_nagle(pcb) == 0) &&
        ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)) {
      break;
    }
#if TCP_CWND_DEBUG
    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"TCPWNDSIZE_F", cwnd %"TCPWNDSIZE_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n",
                                 pcb->snd_wnd, pcb->cwnd, wnd,
                                 lwip_ntohl(seg->tcphdr->seqno) + seg->len -
                                 pcb->lastack,
                                 lwip_ntohl(seg->tcphdr->seqno), pcb->lastack, i));
    ++i;
#endif /* TCP_CWND_DEBUG */

    if (pcb->state != SYN_SENT) {
      TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
    }

    err = tcp_output_segment(seg, pcb, netif);
    if (err != ERR_OK) {
      /* segment could not be sent, for whatever reason */
      tcp_set_flags(pcb, TF_NAGLEMEMERR);
      return err;
    }
#if TCP_OVERSIZE_DBGCHECK
    seg->oversize_left = 0;
#endif /* TCP_OVERSIZE_DBGCHECK */
    pcb->unsent = seg->next;
    if (pcb->state != SYN_SENT) {
      tcp_clear_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
    }
    snd_nxt = lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
    if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
      pcb->snd_nxt = snd_nxt;
    }
    /* put segment on unacknowledged list if length > 0 */
    if (TCP_TCPLEN(seg) > 0) {
      seg->next = NULL;
      /* unacked list is empty? */
      if (pcb->unacked == NULL) {
        pcb->unacked = seg;
        useg = seg;
        /* unacked list is not empty? */
      } else {
        /* In the case of fast retransmit, the packet should not go to the tail
         * of the unacked queue, but rather somewhere before it. We need to check for
         * this case. -STJ Jul 27, 2004 */
        if (TCP_SEQ_LT(lwip_ntohl(seg->tcphdr->seqno), lwip_ntohl(useg->tcphdr->seqno))) {
          /* add segment to before tail of unacked list, keeping the list sorted */
          struct tcp_seg **cur_seg = &(pcb->unacked);
          while (*cur_seg &&
                 TCP_SEQ_LT(lwip_ntohl((*cur_seg)->tcphdr->seqno), lwip_ntohl(seg->tcphdr->seqno))) {
            cur_seg = &((*cur_seg)->next );
          }
          seg->next = (*cur_seg);
          (*cur_seg) = seg;
        } else {
          /* add segment to tail of unacked list */
          useg->next = seg;
          useg = useg->next;
        }
      }
      /* do not queue empty segments on the unacked list */
    } else {
      tcp_seg_free(seg);
    }
    seg = pcb->unsent;
  }
#if TCP_OVERSIZE
  if (pcb->unsent == NULL) {
    /* last unsent has been removed, reset unsent_oversize */
    pcb->unsent_oversize = 0;
  }
#endif /* TCP_OVERSIZE */

output_done:
  tcp_clear_flags(pcb, TF_NAGLEMEMERR);
  return ERR_OK;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x28, %rsp
testq %rdi, %rdi
je 0xc2c9
movq %rdi, %rbx
cmpl $0x1, 0x48(%rdi)
je 0xc2e5
leaq 0x10c53(%rip), %rax     # 0x1c980
cmpq %rbx, (%rax)
je 0xc294
movl 0x80(%rbx), %r14d
movl 0x98(%rbx), %eax
cmpw %r14w, %ax
cmovbl %eax, %r14d
movq 0xa8(%rbx), %r12
testq %r12, %r12
je 0xbe50
leaq 0x18(%rbx), %rdx
movq %rbx, %rdi
movq %rbx, %rsi
movq %rdx, 0x10(%rsp)
callq 0xc3d5
movb $-0x4, %bpl
movq %rax, 0x8(%rsp)
testq %rax, %rax
je 0xc296
cmpb $0x6, 0x14(%rbx)
movl (%rbx), %eax
jne 0xbda0
testl %eax, %eax
jne 0xbe02
cmpl $0x0, 0x4(%rbx)
jne 0xbe02
cmpl $0x0, 0x8(%rbx)
jne 0xbe02
cmpl $0x0, 0xc(%rbx)
jne 0xbe02
jmp 0xbda4
testl %eax, %eax
jne 0xbe02
cmpb $0x6, 0x2c(%rbx)
jne 0xbdc3
movq 0x8(%rsp), %rdi
movq 0x10(%rsp), %rsi
callq 0xf74f
testq %rax, %rax
jne 0xbdcc
jmp 0xc296
movq 0x8(%rsp), %rax
addq $0x8, %rax
movb 0x14(%rax), %cl
movb %cl, 0x14(%rbx)
movl (%rax), %edx
movl %edx, (%rbx)
cmpb $0x6, %cl
jne 0xbdf5
movl 0x4(%rax), %ecx
movl %ecx, 0x4(%rbx)
movl 0x8(%rax), %ecx
movl %ecx, 0x8(%rbx)
movl 0xc(%rax), %ecx
movl %ecx, 0xc(%rbx)
movb 0x10(%rax), %al
movb %al, 0x10(%rbx)
jmp 0xbe02
leaq 0x4(%rbx), %rax
xorl %ecx, %ecx
movq %rcx, 0x5(%rax)
movq %rcx, (%rax)
movzwl %r14w, %r13d
movq 0x18(%r12), %rax
movl 0x4(%rax), %edi
callq 0x135bb
subl 0x7c(%rbx), %eax
movzwl 0x10(%r12), %ecx
addl %eax, %ecx
cmpl %r13d, %ecx
jbe 0xbe70
cmpw 0x98(%rbx), %r14w
jne 0xbe50
cmpq $0x0, 0xb0(%rbx)
jne 0xbe50
cmpb $0x0, 0xfd(%rbx)
jne 0xbe50
movw $0x100, 0xfc(%rbx)      # imm = 0x100
movb $0x0, 0xfe(%rbx)
testb $0x2, 0x52(%rbx)
je 0xc290
movq %rbx, %rdi
addq $0x28, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
jmp 0xc34a
movb $0x0, 0xfd(%rbx)
movq 0xb0(%rbx), %rax
testq %rax, %rax
je 0xbe90
movq %rax, %r14
movq (%rax), %rax
testq %rax, %rax
jne 0xbe83
jmp 0xbe93
xorl %r14d, %r14d
leaq 0xb0(%rbx), %r15
movl %r13d, 0x24(%rsp)
movq %r15, 0x18(%rsp)
movq 0x18(%r12), %rax
movl 0x4(%rax), %edi
callq 0x135bb
subl 0x7c(%rbx), %eax
movzwl 0x10(%r12), %ecx
addl %eax, %ecx
cmpl %r13d, %ecx
ja 0xc27b
movq 0x18(%r12), %rax
movzwl 0xc(%rax), %edi
callq 0x135b4
testb $0x4, %al
jne 0xc2ad
cmpq $0x0, (%r15)
je 0xbeea
movzwl 0x52(%rbx), %ecx
testb $0x44, %cl
je 0xbf59
cmpl $0x2, 0x48(%rbx)
je 0xbf0f
movq 0x18(%r12), %rax
movzwl 0xc(%rax), %ebp
movl $0x10, %edi
callq 0x135b4
orw %bp, %ax
movq 0x18(%r12), %rcx
movw %ax, 0xc(%rcx)
movq %r12, %rdi
callq 0xc4f7
testl %eax, %eax
jne 0xc14b
movl 0x5c(%rbx), %edi
callq 0x135bb
movq 0x18(%r12), %rcx
movl %eax, 0x8(%rcx)
movzwl 0x62(%rbx), %edi
callq 0x135b4
movq 0x18(%r12), %rbp
movw %ax, 0xe(%rbp)
movzwl 0x62(%rbx), %eax
addl 0x5c(%rbx), %eax
movl %eax, 0x64(%rbx)
testb $0x1, 0x17(%r12)
jne 0xbfa7
addq $0x14, %rbp
jmp 0xbfd0
movq 0xa8(%rbx), %rax
testq %rax, %rax
je 0xbf7d
cmpq $0x0, (%rax)
jne 0xbeea
movzwl 0x10(%rax), %edx
cmpw 0x6a(%rbx), %dx
jae 0xbeea
cmpw $0x0, 0x9c(%rbx)
je 0xbeea
testb $-0x60, %cl
jne 0xbeea
cmpw $0x5a, 0x9e(%rbx)
jae 0xbeea
jmp 0xc282
movl $0x5b4, %edi            # imm = 0x5B4
movq 0x8(%rsp), %rsi
movq 0x10(%rsp), %rdx
callq 0x7dee
movzwl %ax, %edi
orl $0x2040000, %edi        # imm = 0x2040000
callq 0x135bb
movl %eax, 0x14(%rbp)
addq $0x18, %rbp
cmpw $0x0, 0x68(%rbx)
jns 0xbfdd
movw $0x0, 0x68(%rbx)
cmpl $0x0, 0x6c(%rbx)
movq 0x18(%r12), %rax
jne 0xc004
leaq 0x1092d(%rip), %rcx     # 0x1c91c
movl (%rcx), %ecx
movl %ecx, 0x6c(%rbx)
movl 0x4(%rax), %edi
callq 0x135bb
movl %eax, 0x70(%rbx)
movq 0x18(%r12), %rax
movq 0x8(%r12), %r13
movl 0x8(%r13), %ecx
movl %eax, %edx
subl %ecx, %edx
subw %dx, 0x12(%r13)
movl 0x10(%r13), %r15d
subl %edx, %r15d
movw %r15w, 0x10(%r13)
movq %rax, 0x8(%r13)
movw $0x0, 0x10(%rax)
movzbl 0x17(%r12), %ecx
movl %ecx, %edx
andl $0x1, %edx
leaq (%rax,%rdx,4), %rdx
addq $0x14, %rdx
cmpq %rdx, %rbp
jne 0xc301
testb $0x4, %cl
jne 0xc075
movzwl 0xc(%rax), %edi
callq 0x135b4
shrl $0xa, %eax
andl $0x3c, %eax
cmpw %ax, %r15w
jne 0xc31d
movq 0x8(%r12), %r13
movq 0x18(%r12), %rax
movzwl 0x10(%r13), %r15d
movzwl 0xc(%rax), %edi
callq 0x135b4
shrl $0xa, %eax
andl $0x3c, %eax
movzwl %r15w, %edx
movq %r13, %rdi
movl $0x6, %esi
movl %eax, %ecx
movq %rbx, %r8
movq 0x10(%rsp), %r9
callq 0x13b80
movb 0x16(%r12), %r13b
movzwl 0x14(%r12), %ecx
testb %r13b, %r13b
je 0xc0bf
rolw $0x8, %cx
movw %cx, 0x14(%r12)
movb $0x0, 0x16(%r12)
notl %eax
movzwl %ax, %eax
movzwl %cx, %ecx
addl %eax, %ecx
movl %ecx, %eax
shrl $0x10, %eax
addl %ecx, %eax
notl %eax
movq 0x18(%r12), %rcx
movw %ax, 0x10(%rcx)
cmpb $0x6, 0x2c(%rbx)
movq 0x8(%r12), %rdi
movzbl 0x33(%rbx), %ecx
movzbl 0x32(%rbx), %r8d
jne 0xc10d
movq 0x8(%rsp), %rax
movq %rax, (%rsp)
movq %rbx, %rsi
movq 0x10(%rsp), %rdx
movl $0x6, %r9d
callq 0xff25
jmp 0xc129
movq 0x8(%rsp), %rax
movq %rax, (%rsp)
movq %rbx, %rsi
movq 0x10(%rsp), %rdx
movl $0x6, %r9d
callq 0xe33b
movl %eax, %ebp
testb %r13b, %r13b
je 0xc13d
rolw $0x8, 0x14(%r12)
movb $0x1, 0x16(%r12)
testb %bpl, %bpl
movl 0x24(%rsp), %r13d
jne 0xc2a7
movw $0x0, 0x12(%r12)
movq (%r12), %rax
movq %rax, 0xa8(%rbx)
cmpl $0x2, 0x48(%rbx)
je 0xc168
andb $-0x4, 0x52(%rbx)
movq 0x18(%r12), %rax
movl 0x4(%rax), %edi
callq 0x135bb
movl %eax, %ebp
movzwl 0x10(%r12), %r15d
movq 0x18(%r12), %rax
movzwl 0xc(%rax), %edi
callq 0x135b4
andl $0x3, %eax
addl %ebp, %r15d
cmpw $0x1, %ax
sbbl $-0x1, %r15d
cmpl %r15d, 0x88(%rbx)
jns 0xc1a9
movl %r15d, 0x88(%rbx)
movzwl 0x10(%r12), %ebp
movq 0x18(%r12), %rax
movzwl 0xc(%rax), %edi
callq 0x135b4
xorl %ecx, %ecx
testb $0x3, %al
setne %cl
orw %bp, %cx
je 0xc200
movq $0x0, (%r12)
movq 0x18(%rsp), %r15
cmpq $0x0, (%r15)
je 0xc20f
movq 0x18(%r12), %rax
movl 0x4(%rax), %edi
callq 0x135bb
movl %eax, %ebp
movq 0x18(%r14), %rax
movl 0x4(%rax), %edi
callq 0x135bb
cmpl %eax, %ebp
js 0xc217
movq %r12, (%r14)
jmp 0xc212
movq %r12, %rdi
callq 0x817c
movq 0x18(%rsp), %r15
jmp 0xc269
movq %r12, (%r15)
movq %r12, %r14
jmp 0xc269
movq (%r15), %rcx
testq %rcx, %rcx
je 0xc250
movq 0x18(%rcx), %rax
movl 0x4(%rax), %edi
callq 0x135bb
movl %eax, %ebp
movq 0x18(%r12), %rax
movl 0x4(%rax), %edi
callq 0x135bb
cmpl %eax, %ebp
movq (%r15), %rax
jns 0xc257
movq (%rax), %rcx
movq %rax, %r15
testq %rcx, %rcx
jne 0xc21f
xorl %ecx, %ecx
jmp 0xc25d
xorl %ecx, %ecx
movq %r15, %rax
jmp 0xc262
movq %rax, %rcx
movq %r15, %rax
movq 0x18(%rsp), %r15
movq %rcx, (%r12)
movq %r12, (%rax)
movq 0xa8(%rbx), %r12
testq %r12, %r12
jne 0xbea4
jmp 0xc287
movq 0xa8(%rbx), %rax
testq %rax, %rax
jne 0xc290
movw $0x0, 0xa0(%rbx)
andb $0x7f, 0x52(%rbx)
xorl %ebp, %ebp
movl %ebp, %eax
addq $0x28, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
orb $-0x80, 0x52(%rbx)
jmp 0xc296
leaq 0x8018(%rip), %rdi      # 0x142cc
leaq 0x98dc(%rip), %rsi      # 0x15b97
leaq 0x94bd(%rip), %rcx      # 0x1577f
movl $0x537, %edx            # imm = 0x537
jmp 0xc337
leaq 0x7ffc(%rip), %rdi      # 0x142cc
leaq 0x9882(%rip), %rsi      # 0x15b59
leaq 0x94a1(%rip), %rcx      # 0x1577f
movl $0x4e1, %edx            # imm = 0x4E1
jmp 0xc337
leaq 0x7fe0(%rip), %rdi      # 0x142cc
leaq 0x987e(%rip), %rsi      # 0x15b71
leaq 0x9485(%rip), %rcx      # 0x1577f
movl $0x4e4, %edx            # imm = 0x4E4
jmp 0xc337
leaq 0x7fc4(%rip), %rdi      # 0x142cc
leaq 0x9ad5(%rip), %rsi      # 0x15de4
leaq 0x9469(%rip), %rcx      # 0x1577f
movl $0x61c, %edx            # imm = 0x61C
jmp 0xc337
leaq 0x7fa8(%rip), %rdi      # 0x142cc
leaq 0x9acc(%rip), %rsi      # 0x15df7
leaq 0x944d(%rip), %rcx      # 0x1577f
movl $0x628, %edx            # imm = 0x628
xorl %eax, %eax
callq 0x20d0
xorl %edi, %edi
callq 0x21b0
callq 0x2050