// File: /workspace/github_c_makefile_star2_O2/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 0xbd9f
movq %rdi, %rbx
cmpl $0x1, 0x48(%rdi)
je 0xbdbb
leaq 0x12151(%rip), %rax     # 0x1d980
cmpq %rbx, (%rax)
je 0xbd68
movl 0x80(%rbx), %r14d
movl 0x98(%rbx), %eax
cmpw %r14w, %ax
cmovbl %eax, %r14d
movq 0xa8(%rbx), %r12
testq %r12, %r12
je 0xb955
leaq 0x18(%rbx), %rdx
movq %rbx, %rdi
movq %rbx, %rsi
movq %rdx, 0x10(%rsp)
callq 0xbeab
movb $-0x4, %r13b
movq %rax, 0x8(%rsp)
testq %rax, %rax
je 0xbd6b
cmpb $0x6, 0x14(%rbx)
movl (%rbx), %eax
jne 0xb8a2
testl %eax, %eax
jne 0xb904
cmpl $0x0, 0x4(%rbx)
jne 0xb904
cmpl $0x0, 0x8(%rbx)
jne 0xb904
cmpl $0x0, 0xc(%rbx)
jne 0xb904
jmp 0xb8a6
testl %eax, %eax
jne 0xb904
cmpb $0x6, 0x2c(%rbx)
jne 0xb8c5
movq 0x8(%rsp), %rdi
movq 0x10(%rsp), %rsi
callq 0xf14f
testq %rax, %rax
jne 0xb8ce
jmp 0xbd6b
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 0xb8f7
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 0xb904
leaq 0x4(%rbx), %rax
andq $0x0, 0x5(%rax)
andq $0x0, (%rax)
movzwl %r14w, %ebp
movq 0x18(%r12), %rax
movl 0x4(%rax), %edi
callq 0x12ed7
subl 0x7c(%rbx), %eax
movzwl 0x10(%r12), %ecx
addl %eax, %ecx
movl %ebp, 0x24(%rsp)
cmpl %ebp, %ecx
jbe 0xb975
cmpw 0x98(%rbx), %r14w
jne 0xb955
cmpq $0x0, 0xb0(%rbx)
jne 0xb955
cmpb $0x0, 0xfd(%rbx)
jne 0xb955
movw $0x100, 0xfc(%rbx)      # imm = 0x100
movb $0x0, 0xfe(%rbx)
testb $0x2, 0x52(%rbx)
je 0xbd64
movq %rbx, %rdi
addq $0x28, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
jmp 0xbe20
movb $0x0, 0xfd(%rbx)
movq 0xb0(%rbx), %rax
testq %rax, %rax
je 0xb995
movq %rax, %r15
movq (%rax), %rax
testq %rax, %rax
jne 0xb988
jmp 0xb998
xorl %r15d, %r15d
leaq 0xb0(%rbx), %r14
movq %r14, 0x18(%rsp)
testq %r12, %r12
je 0xbd52
movq 0x18(%r12), %rax
movl 0x4(%rax), %edi
callq 0x12ed7
subl 0x7c(%rbx), %eax
movzwl 0x10(%r12), %ecx
addl %eax, %ecx
cmpl 0x24(%rsp), %ecx
ja 0xbd52
movq 0x18(%r12), %rax
movzwl 0xc(%rax), %edi
callq 0x12ed0
testb $0x4, %al
jne 0xbd83
cmpq $0x0, (%r14)
je 0xb9f3
movzwl 0x52(%rbx), %eax
testb $0x44, %al
je 0xba60
cmpl $0x2, 0x48(%rbx)
je 0xba16
movq 0x18(%r12), %rax
movzwl 0xc(%rax), %ebp
pushq $0x10
popq %rdi
callq 0x12ed0
orw %bp, %ax
movq 0x18(%r12), %rcx
movw %ax, 0xc(%rcx)
movq %r12, %rdi
callq 0xbfc7
testl %eax, %eax
jne 0xbc46
movl 0x5c(%rbx), %edi
callq 0x12ed7
movq 0x18(%r12), %rcx
movl %eax, 0x8(%rcx)
movzwl 0x62(%rbx), %edi
callq 0x12ed0
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 0xbaa9
addq $0x14, %rbp
jmp 0xbad2
movq 0xa8(%rbx), %rcx
testq %rcx, %rcx
je 0xba80
cmpq $0x0, (%rcx)
jne 0xb9f3
movzwl 0x10(%rcx), %ecx
cmpw 0x6a(%rbx), %cx
jae 0xb9f3
cmpw $0x0, 0x9c(%rbx)
je 0xb9f3
testb $-0x60, %al
jne 0xb9f3
cmpw $0x5a, 0x9e(%rbx)
jae 0xb9f3
jmp 0xbd52
movl $0x5b4, %edi            # imm = 0x5B4
movq 0x8(%rsp), %rsi
movq 0x10(%rsp), %rdx
callq 0x7b16
movzwl %ax, %edi
orl $0x2040000, %edi        # imm = 0x2040000
callq 0x12ed7
movl %eax, 0x14(%rbp)
addq $0x18, %rbp
cmpw $0x0, 0x68(%rbx)
jns 0xbade
andw $0x0, 0x68(%rbx)
cmpl $0x0, 0x6c(%rbx)
movq 0x18(%r12), %rax
jne 0xbb05
leaq 0x11e2c(%rip), %rcx     # 0x1d91c
movl (%rcx), %ecx
movl %ecx, 0x6c(%rbx)
movl 0x4(%rax), %edi
callq 0x12ed7
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), %r14d
subl %edx, %r14d
movw %r14w, 0x10(%r13)
movq %rax, 0x8(%r13)
andw $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 0xbdd7
testb $0x4, %cl
jne 0xbb75
movzwl 0xc(%rax), %edi
callq 0x12ed0
shrl $0xa, %eax
andl $0x3c, %eax
cmpw %ax, %r14w
jne 0xbdf3
movq 0x8(%r12), %r13
movq 0x18(%r12), %rax
movzwl 0x10(%r13), %r14d
movzwl 0xc(%rax), %edi
callq 0x12ed0
shrl $0xa, %eax
andl $0x3c, %eax
movzwl %r14w, %edx
movq %r13, %rdi
pushq $0x6
popq %rsi
movl %eax, %ecx
movq %rbx, %r8
movq 0x10(%rsp), %r9
callq 0x133b9
movb 0x16(%r12), %bpl
movzwl 0x14(%r12), %ecx
testb %bpl, %bpl
je 0xbbbd
rolw $0x8, %cx
movw %cx, 0x14(%r12)
movb $0x0, 0x16(%r12)
movq 0x18(%rsp), %r14
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 0xbc0e
movq 0x8(%rsp), %rax
movq %rax, (%rsp)
movq %rbx, %rsi
movq 0x10(%rsp), %rdx
pushq $0x6
popq %r9
callq 0xf908
jmp 0xbc28
movq 0x8(%rsp), %rax
movq %rax, (%rsp)
movq %rbx, %rsi
movq 0x10(%rsp), %rdx
pushq $0x6
popq %r9
callq 0xdd30
movl %eax, %r13d
testb %bpl, %bpl
je 0xbc3d
rolw $0x8, 0x14(%r12)
movb $0x1, 0x16(%r12)
testb %r13b, %r13b
jne 0xbd7d
andw $0x0, 0x12(%r12)
movq (%r12), %rax
movq %rax, 0xa8(%rbx)
cmpl $0x2, 0x48(%rbx)
je 0xbc62
andb $-0x4, 0x52(%rbx)
movq 0x18(%r12), %rax
movl 0x4(%rax), %edi
callq 0x12ed7
movl %eax, %r13d
movzwl 0x10(%r12), %ebp
movq 0x18(%r12), %rax
movzwl 0xc(%rax), %edi
callq 0x12ed0
andl $0x3, %eax
addl %r13d, %ebp
cmpw $0x1, %ax
sbbl $-0x1, %ebp
cmpl %ebp, 0x88(%rbx)
jns 0xbca1
movl %ebp, 0x88(%rbx)
movzwl 0x10(%r12), %ebp
movq 0x18(%r12), %rax
movzwl 0xc(%rax), %edi
callq 0x12ed0
xorl %ecx, %ecx
testb $0x3, %al
setne %cl
orw %bp, %cx
je 0xbcf2
andq $0x0, (%r12)
cmpq $0x0, (%r14)
je 0xbcfc
movq 0x18(%r12), %rax
movl 0x4(%rax), %edi
callq 0x12ed7
movl %eax, %r13d
movq 0x18(%r15), %rax
movl 0x4(%rax), %edi
callq 0x12ed7
cmpl %eax, %r13d
js 0xbd04
movq %r12, (%r15)
jmp 0xbcff
movq %r12, %rdi
callq 0x7e33
jmp 0xbd46
movq %r12, (%r14)
movq %r12, %r15
jmp 0xbd46
movq %r14, %rax
movq %rax, %r14
movq (%rax), %rax
testq %rax, %rax
je 0xbd38
movq 0x18(%rax), %rax
movl 0x4(%rax), %edi
callq 0x12ed7
movl %eax, %r13d
movq 0x18(%r12), %rax
movl 0x4(%rax), %edi
callq 0x12ed7
cmpl %eax, %r13d
movq (%r14), %rax
js 0xbd07
jmp 0xbd3a
xorl %eax, %eax
movq %rax, (%r12)
movq %r12, (%r14)
movq 0x18(%rsp), %r14
movq 0xa8(%rbx), %r12
jmp 0xb9a4
cmpq $0x0, 0xa8(%rbx)
jne 0xbd64
andw $0x0, 0xa0(%rbx)
andb $0x7f, 0x52(%rbx)
xorl %r13d, %r13d
movl %r13d, %eax
addq $0x28, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
orb $-0x80, 0x52(%rbx)
jmp 0xbd6b
leaq 0x8542(%rip), %rdi      # 0x142cc
leaq 0x9e1e(%rip), %rsi      # 0x15baf
leaq 0x99ff(%rip), %rcx      # 0x15797
movl $0x537, %edx            # imm = 0x537
jmp 0xbe0d
leaq 0x8526(%rip), %rdi      # 0x142cc
leaq 0x9dc4(%rip), %rsi      # 0x15b71
leaq 0x99e3(%rip), %rcx      # 0x15797
movl $0x4e1, %edx            # imm = 0x4E1
jmp 0xbe0d
leaq 0x850a(%rip), %rdi      # 0x142cc
leaq 0x9dc0(%rip), %rsi      # 0x15b89
leaq 0x99c7(%rip), %rcx      # 0x15797
movl $0x4e4, %edx            # imm = 0x4E4
jmp 0xbe0d
leaq 0x84ee(%rip), %rdi      # 0x142cc
leaq 0xa017(%rip), %rsi      # 0x15dfc
leaq 0x99ab(%rip), %rcx      # 0x15797
movl $0x61c, %edx            # imm = 0x61C
jmp 0xbe0d
leaq 0x84d2(%rip), %rdi      # 0x142cc
leaq 0xa00e(%rip), %rsi      # 0x15e0f
leaq 0x998f(%rip), %rcx      # 0x15797
movl $0x628, %edx            # imm = 0x628
xorl %eax, %eax
callq 0x20d0
xorl %edi, %edi
callq 0x21b0
callq 0x2050