// File: /workspace/github_c_makefile_star2_O2/nh13[P]ksw/src/ksw2/kalloc.c
// Function: kfree
void kfree(void *_km, void *ap)
{
	size_t *p, *q;
	kmem_t *km = (kmem_t*)_km;
	
	if (!ap) return;
	if (km == 0) {
		free(ap);
		return;
	}
	p = (size_t*)ap - 1; /* *p is the size of the current block */
	/* Find the pointer that points to the block to be freed. The following loop can stop on two conditions:
	 *
	 * a) "p>q && p<q->ptr": @------@++++++++@+++++++@-------    @---------------@+++++++@-------
	 *    (can also be in    |      |                |        -> |                       |
	 *     two cores)        q      p           q->ptr           q                  q->ptr
	 *
	 *                       @--------    @+++++++++@--------    @--------    @------------------
	 *                       |            |         |         -> |            |
	 *                       q            p    q->ptr            q       q->ptr
	 *
	 * b) "q>=q->ptr && (p>q || p<q->ptr)":  @-------@+++++   @--------@+++++++     @-------@+++++   @----------------
	 *                                       |                |        |         -> |                |
	 *                                  q->ptr                q        p       q->ptr                q
	 *
	 *                                       @+++++++@-----   @++++++++@-------     @-------------   @++++++++@-------
	 *                                       |       |                 |         -> |                         |
	 *                                       p  q->ptr                 q       q->ptr                         q
	 */
	for (q = km->loop_head; !(p > q && p < PTR(q)); q = PTR(q))
		if (q >= PTR(q) && (p > q || p < PTR(q))) break;
	if (p + (*p) == PTR(q)) { /* two adjacent blocks, merge p and q->ptr (the 2nd and 4th cases) */
		*p += *PTR(q); /* this is the new q->ptr size */
		p[1] = (size_t)PTR(PTR(q)); /* this is the new q->ptr->ptr */
		/* p is actually the new q->ptr. The actual change happens a few lines below. */
	} else if (p + (*p) > PTR(q) && PTR(q) >= p) { /* the end of the allocated block is in the next free block */
		kerror("[kfree] The end of the allocated block enters a free block.");
	} else p[1] = (size_t)PTR(q); /* backup q->ptr */

	if (q + (*q) == p) { /* two adjacent blocks, merge q and p (the other two cases) */
		*q += *p;
		q[1] = (size_t)PTR(p);
		km->loop_head = q;
	} else if (q + (*q) > p && p >= q) { /* the end of a free block in the allocated block */
		kerror("[kfree] The end of a free block enters the allocated block.");
	} else km->loop_head = p, q[1] = (size_t)p; /* in two cores, cannot be merged */
}

// Assembly:
pushq %rax
testq %rsi, %rsi
je 0x19e44d
testq %rdi, %rdi
je 0x19e3fd
leaq -0x8(%rsi), %rcx
movq 0x10(%rdi), %rdx
movq %rdx, %rax
cmpq %rdx, %rcx
seta %r8b
movq 0x8(%rdx), %rdx
cmpq %rdx, %rcx
setb %r9b
testb %r9b, %r8b
jne 0x19e3d5
cmpq %rax, %rcx
seta %r8b
cmpq %rdx, %rcx
setb %r9b
cmpq %rdx, %rax
jb 0x19e3a3
orb %r9b, %r8b
je 0x19e3a3
movq (%rcx), %r8
leaq (%rcx,%r8,8), %r9
cmpq %rdx, %r9
je 0x19e406
setbe %r9b
cmpq %rdx, %rcx
seta %r10b
orb %r9b, %r10b
jne 0x19e414
leaq 0x3c9c20(%rip), %rdi    # 0x568018
callq 0x19e45b
movq %rsi, %rdi
popq %rax
jmp 0x19e120
addq (%rdx), %r8
movq %r8, (%rcx)
movq 0x8(%rax), %rdx
movq 0x8(%rdx), %rdx
movq %rdx, (%rsi)
movq (%rax), %rdx
leaq (%rax,%rdx,8), %r9
cmpq %rcx, %r9
je 0x19e43c
setbe %dl
cmpq %rax, %rcx
setb %sil
orb %dl, %sil
je 0x19e44f
movq %rcx, 0x10(%rdi)
movq %rcx, 0x8(%rax)
jmp 0x19e44d
addq %rdx, %r8
movq %r8, (%rax)
movq (%rsi), %rcx
movq %rcx, 0x8(%rax)
movq %rax, 0x10(%rdi)
popq %rax
retq
leaq 0x3c9bfe(%rip), %rdi    # 0x568054
callq 0x19e45b