// File: chahk0129[P]hash_bench/./index/extendible_hash.h
// Function: ExtendibleHash<char [32]>::Insert(char (&) [32], long)
void ExtendibleHash<Key_t>::Insert(Key_t& key, Value_t value) {
    size_t f_hash;
    if constexpr(sizeof(Key_t) > 8)
	f_hash = hash_funcs[0](key, sizeof(Key_t), f_seed);
    else
	f_hash = hash_funcs[0](&key, sizeof(Key_t), f_seed);
    auto f_idx = (f_hash & kMask) * kNumPairPerCacheLine;

RETRY:
    auto x = (f_hash >> (8*sizeof(f_hash) - dir->depth));
    auto target = dir->_[x];

    if(!target){
	std::this_thread::yield();
	goto RETRY;
    }

    /* acquire segment exclusive lock */
    if(!target->mutex.try_lock()){
	std::this_thread::yield();
	goto RETRY;
    }

    auto target_check = (f_hash >> (8*sizeof(f_hash) - dir->depth));
    if(target != dir->_[target_check]){
	target->mutex.unlock();
	std::this_thread::yield();
	goto RETRY;
    }

    auto target_local_depth = target->local_depth;
    auto pattern = (f_hash >> (8*sizeof(f_hash) - target->local_depth));
    for(unsigned i=0; i<kNumPairPerCacheLine * kNumCacheLine; ++i){
	auto loc = (f_idx + i) % Segment<Key_t>::kNumSlot;
	if constexpr(sizeof(Key_t) > 8){
	    if((((hash_funcs[0](target->_[loc].key, sizeof(Key_t), f_seed) >> (8*sizeof(f_hash)-target_local_depth)) != pattern) ||
			(memcmp(target->_[loc].key, INVALID<Key_t>, sizeof(Key_t)) == 0))){
		memcpy(target->_[loc].key, key, sizeof(Key_t));
		memcpy(&target->_[loc].value, &value, sizeof(Value_t));
		target->mutex.unlock();
		return;
	    }
	}
	else{
	    if((((hash_funcs[0](&target->_[loc].key, sizeof(Key_t), f_seed) >> (8*sizeof(f_hash)-target_local_depth)) != pattern) ||
			(memcmp(&target->_[loc].key, &INVALID<Key_t>, sizeof(Key_t)) == 0))){
		memcpy(&target->_[loc].key, &key, sizeof(Key_t));
		memcpy(&target->_[loc].value, &value, sizeof(Value_t));
		target->mutex.unlock();
		return;
	    }
	}

    }

#ifdef S_HASH
    size_t s_hash;
    if constexpr(sizeof(Key_t) > 8)
	s_hash = hash_funcs[2](key, sizeof(Key_t), s_seed);
    else
	s_hash = hash_funcs[2](&key, sizeof(Key_t), s_seed);
    auto s_idx = (s_hash & kMask) * kNumPairPerCacheLine;

    for(unsigned i=0; i<kNumPairPerCacheLine * kNumCacheLine; ++i){
	auto loc = (s_idx + i) % Segment<Key_t>::kNumSlot;
	if constexpr(sizeof(Key_t) > 8){
	    if((((hash_funcs[0](target->_[loc].key, sizeof(Key_t), f_seed) >> (8*sizeof(f_hash)-target_local_depth)) != pattern) ||
			(memcmp(target->_[loc].key, INVALID<Key_t>, sizeof(Key_t)) == 0))){
		memcpy(target->_[loc].key, key, sizeof(Key_t));
		memcpy(&target->_[loc].value, &value, sizeof(Value_t));
		target->mutex.unlock();
		return;
	    }
	}
	else{
	    if((((hash_funcs[0](&target->_[loc].key, sizeof(Key_t), f_seed) >> (8*sizeof(f_hash)-target_local_depth)) != pattern) ||
			(memcmp(&target->_[loc].key, &INVALID<Key_t>, sizeof(Key_t)) == 0))){
		memcpy(&target->_[loc].key, &key, sizeof(Key_t));
		memcpy(&target->_[loc].value, &value, sizeof(Value_t));
		target->mutex.unlock();
		return;
	    }
	}
    }
#endif

    // COLLISION!!
    /* need to split segment but release the exclusive lock first to avoid deadlock */

    /* need to check whether the target segment has been split */
#ifdef BREAKDOWN
    clock_gettime(CLOCK_MONOTONIC, &t_start);
#endif
    Segment<Key_t>** s = target->Split();

DIR_RETRY:
    /* need to double the directory */
    if(target_local_depth == dir->depth){
	if(!dir->suspend()){
	    std::this_thread::yield();
	    goto DIR_RETRY;
	}

	x = (f_hash >> (8*sizeof(f_hash) - dir->depth));
	auto dir_old = dir;
	auto d = dir->_;
	auto _dir = new Directory<Key_t>(dir->depth+1);
	for(unsigned i = 0; i < dir->capacity; ++i){
	    if (i == x){
		_dir->_[2*i] = s[0];
		_dir->_[2*i+1] = s[1];
	    }
	    else{
		_dir->_[2*i] = d[i];
		_dir->_[2*i+1] = d[i];
	    }
	}
	dir = _dir;
#ifdef INPLACE
	s[0]->local_depth++;
	s[0]->mutex.unlock();
#endif
    }
    else{ // normal segment split
	while(!dir->lock()){
	    asm("nop");
	}

	x = (f_hash >> (8 * sizeof(f_hash) - dir->depth));
	if(dir->depth == target_local_depth + 1){
	    if(x%2 == 0){
		dir->_[x+1] = s[1];
#ifndef INPLACE
		dir->_[x] = s[0];
#endif
	    }
	    else{
		dir->_[x] = s[1];
#ifndef INPLACE
		dir->_[x-1] = s[0];
#endif
	    }	    
	    dir->unlock();
#ifdef INPLACE
	    s[0]->local_depth++;
	    /* release target segment exclusive lock */
	    s[0]->mutex.unlock();
#endif
	}
	else{
	    int stride = pow(2, dir->depth - target_local_depth);
	    auto loc = x - (x%stride);
	    for(int i=0; i<stride/2; ++i){
		dir->_[loc+stride/2+i] = s[1];
	    }
#ifndef INPLACE
	    for(int i=0; i<stride/2; ++i){
		dir->_[loc+i] = s[0];
	    }
#endif
	    dir->unlock();
#ifdef INPLACE
	    s[0]->local_depth++;
	    /* release target segment exclusive lock */
	    s[0]->mutex.unlock();
#endif
	}
    }
#ifdef BREAKDOWN
    clock_gettime(CLOCK_MONOTONIC, &t_end);
    split_time += t_end.tv_nsec - t_start.tv_nsec + (t_end.tv_sec - t_start.tv_sec)*1000000000;
#endif
    std::this_thread::yield();
    goto RETRY;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0xe0, %rsp
movq %rdi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
movq %rdx, -0x18(%rbp)
movq -0x8(%rbp), %rax
movq %rax, -0xb0(%rbp)
movq 0x3e7a7(%rip), %rax     # 0x5a480
movq -0x10(%rbp), %rdi
movl $0x20, %esi
movl $0xc70697, %edx         # imm = 0xC70697
callq *%rax
movq %rax, -0x20(%rbp)
movq -0x20(%rbp), %rax
andq $0xff, %rax
shlq $0x2, %rax
movq %rax, -0x28(%rbp)
movq -0xb0(%rbp), %rax
movq -0x20(%rbp), %rdx
movq 0x8(%rax), %rsi
movl $0x40, %ecx
subq 0x18(%rsi), %rcx
shrq %cl, %rdx
movq %rdx, %rcx
movq %rcx, -0x30(%rbp)
movq 0x8(%rax), %rax
movq (%rax), %rax
movq -0x30(%rbp), %rcx
movq (%rax,%rcx,8), %rax
movq %rax, -0x38(%rbp)
cmpq $0x0, -0x38(%rbp)
jne 0x1bd42
callq 0x1cbf0
jmp 0x1bcff
movq -0x38(%rbp), %rdi
addq $0xa008, %rdi           # imm = 0xA008
callq 0x1cc10
testb $0x1, %al
jne 0x1bd5d
callq 0x1cbf0
jmp 0x1bcff
movq -0xb0(%rbp), %rcx
movq -0x20(%rbp), %rax
movq 0x8(%rcx), %rdx
movl $0x40, %ecx
subq 0x18(%rdx), %rcx
shrq %cl, %rax
movq -0xb0(%rbp), %rcx
movq %rax, -0x40(%rbp)
movq -0x38(%rbp), %rax
movq 0x8(%rcx), %rcx
movq (%rcx), %rcx
movq -0x40(%rbp), %rdx
cmpq (%rcx,%rdx,8), %rax
je 0x1bdb2
movq -0x38(%rbp), %rdi
addq $0xa008, %rdi           # imm = 0xA008
callq 0x1cc30
callq 0x1cbf0
jmp 0x1bcff
movq -0x38(%rbp), %rax
movq 0xa000(%rax), %rax
movq %rax, -0x48(%rbp)
movq -0x20(%rbp), %rax
movq -0x38(%rbp), %rdx
movl $0x40, %ecx
subq 0xa000(%rdx), %rcx
shrq %cl, %rax
movq %rax, -0x50(%rbp)
movl $0x0, -0x54(%rbp)
movl -0x54(%rbp), %eax
cmpq $0x400, %rax            # imm = 0x400
jae 0x1bec4
movq -0x28(%rbp), %rax
movl -0x54(%rbp), %ecx
addq %rcx, %rax
andq $0x3ff, %rax            # imm = 0x3FF
movq %rax, -0x60(%rbp)
movq 0x3e673(%rip), %rax     # 0x5a480
movq -0x38(%rbp), %rdi
imulq $0x28, -0x60(%rbp), %rcx
addq %rcx, %rdi
movl $0x20, %esi
movl $0xc70697, %edx         # imm = 0xC70697
callq *%rax
movl $0x40, %ecx
subq -0x48(%rbp), %rcx
shrq %cl, %rax
cmpq -0x50(%rbp), %rax
jne 0x1be59
movq -0x38(%rbp), %rdi
imulq $0x28, -0x60(%rbp), %rax
addq %rax, %rdi
leaq 0x3ee56(%rip), %rsi     # 0x5aca0
movl $0x20, %edx
callq 0x6190
cmpl $0x0, %eax
jne 0x1beb4
movq -0x38(%rbp), %rax
imulq $0x28, -0x60(%rbp), %rcx
addq %rcx, %rax
movq -0x10(%rbp), %rcx
movq (%rcx), %rdx
movq %rdx, (%rax)
movq 0x8(%rcx), %rdx
movq %rdx, 0x8(%rax)
movq 0x10(%rcx), %rdx
movq %rdx, 0x10(%rax)
movq 0x18(%rcx), %rcx
movq %rcx, 0x18(%rax)
movq -0x38(%rbp), %rax
imulq $0x28, -0x60(%rbp), %rcx
addq %rcx, %rax
movq -0x18(%rbp), %rcx
movq %rcx, 0x20(%rax)
movq -0x38(%rbp), %rdi
addq $0xa008, %rdi           # imm = 0xA008
callq 0x1cc30
addq $0xe0, %rsp
popq %rbp
retq
jmp 0x1beb6
movl -0x54(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x54(%rbp)
jmp 0x1bde3
movl $0x1, %edi
leaq 0x3eda0(%rip), %rsi     # 0x5ac70
callq 0x63d0
movq -0x38(%rbp), %rdi
callq 0x1cc50
movq %rax, -0x68(%rbp)
movq -0xb0(%rbp), %rcx
movq -0x48(%rbp), %rax
movq 0x8(%rcx), %rcx
cmpq 0x18(%rcx), %rax
jne 0x1c08c
movq -0xb0(%rbp), %rax
movq 0x8(%rax), %rdi
callq 0x1cec0
testb $0x1, %al
jne 0x1bf16
callq 0x1cbf0
jmp 0x1bee2
movq -0xb0(%rbp), %rax
movq -0x20(%rbp), %rdx
movq 0x8(%rax), %rcx
movb 0x18(%rcx), %cl
negb %cl
shrq %cl, %rdx
movq %rdx, %rcx
movq %rcx, -0x30(%rbp)
movq 0x8(%rax), %rcx
movq %rcx, -0x70(%rbp)
movq 0x8(%rax), %rax
movq (%rax), %rax
movq %rax, -0x78(%rbp)
movl $0x20, %edi
callq 0x6420
movq %rax, %rdi
movq -0xb0(%rbp), %rax
movq %rdi, %rcx
movq %rcx, -0xb8(%rbp)
movq 0x8(%rax), %rax
movq 0x18(%rax), %rsi
incq %rsi
callq 0x1bba0
jmp 0x1bf77
movq -0xb8(%rbp), %rax
movq %rax, -0x80(%rbp)
movl $0x0, -0x90(%rbp)
movq -0xb0(%rbp), %rcx
movl -0x90(%rbp), %eax
movq 0x8(%rcx), %rcx
cmpq 0x10(%rcx), %rax
jae 0x1c078
movl -0x90(%rbp), %eax
cmpq -0x30(%rbp), %rax
jne 0x1c019
movq -0x68(%rbp), %rax
movq (%rax), %rdx
movq -0x80(%rbp), %rax
movq (%rax), %rax
movl -0x90(%rbp), %ecx
shll %ecx
movl %ecx, %ecx
movq %rdx, (%rax,%rcx,8)
movq -0x68(%rbp), %rax
movq 0x8(%rax), %rdx
movq -0x80(%rbp), %rax
movq (%rax), %rax
movl -0x90(%rbp), %ecx
shll %ecx
addl $0x1, %ecx
movl %ecx, %ecx
movq %rdx, (%rax,%rcx,8)
jmp 0x1c062
movq -0xb8(%rbp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x88(%rbp)
movl %eax, -0x8c(%rbp)
movl $0x20, %esi
callq 0x6430
jmp 0x1c336
movq -0x78(%rbp), %rax
movl -0x90(%rbp), %ecx
movq (%rax,%rcx,8), %rdx
movq -0x80(%rbp), %rax
movq (%rax), %rax
movl -0x90(%rbp), %ecx
shll %ecx
movl %ecx, %ecx
movq %rdx, (%rax,%rcx,8)
movq -0x78(%rbp), %rax
movl -0x90(%rbp), %ecx
movq (%rax,%rcx,8), %rdx
movq -0x80(%rbp), %rax
movq (%rax), %rax
movl -0x90(%rbp), %ecx
shll %ecx
addl $0x1, %ecx
movl %ecx, %ecx
movq %rdx, (%rax,%rcx,8)
jmp 0x1c064
movl -0x90(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x90(%rbp)
jmp 0x1bf8c
movq -0xb0(%rbp), %rax
movq -0x80(%rbp), %rcx
movq %rcx, 0x8(%rax)
jmp 0x1c2e7
jmp 0x1c08e
movq -0xb0(%rbp), %rax
movq 0x8(%rax), %rdi
callq 0x1cf80
xorb $-0x1, %al
testb $0x1, %al
jne 0x1c0a6
jmp 0x1c0a9
nop
jmp 0x1c08e
movq -0xb0(%rbp), %rax
movq -0x20(%rbp), %rdx
movq 0x8(%rax), %rsi
movl $0x40, %ecx
subq 0x18(%rsi), %rcx
shrq %cl, %rdx
movq %rdx, %rcx
movq %rcx, -0x30(%rbp)
movq 0x8(%rax), %rax
movq 0x18(%rax), %rax
movq -0x48(%rbp), %rcx
addq $0x1, %rcx
cmpq %rcx, %rax
jne 0x1c176
movq -0x30(%rbp), %rax
andq $0x1, %rax
cmpq $0x0, %rax
jne 0x1c129
movq -0xb0(%rbp), %rax
movq -0x68(%rbp), %rcx
movq 0x8(%rcx), %rsi
movq 0x8(%rax), %rcx
movq (%rcx), %rcx
movq -0x30(%rbp), %rdx
movq %rsi, 0x8(%rcx,%rdx,8)
movq -0x68(%rbp), %rcx
movq (%rcx), %rdx
movq 0x8(%rax), %rax
movq (%rax), %rax
movq -0x30(%rbp), %rcx
movq %rdx, (%rax,%rcx,8)
jmp 0x1c161
movq -0xb0(%rbp), %rax
movq -0x68(%rbp), %rcx
movq 0x8(%rcx), %rsi
movq 0x8(%rax), %rcx
movq (%rcx), %rcx
movq -0x30(%rbp), %rdx
movq %rsi, (%rcx,%rdx,8)
movq -0x68(%rbp), %rcx
movq (%rcx), %rdx
movq 0x8(%rax), %rax
movq (%rax), %rax
movq -0x30(%rbp), %rcx
subq $0x1, %rcx
movq %rdx, (%rax,%rcx,8)
movq -0xb0(%rbp), %rax
movq 0x8(%rax), %rdi
callq 0x1d010
jmp 0x1c2e5
movq -0xb0(%rbp), %rax
movq 0x8(%rax), %rax
movq 0x18(%rax), %rsi
subq -0x48(%rbp), %rsi
movl $0x2, %edi
callq 0x1cb40
cvttsd2si %xmm0, %eax
movl %eax, -0x94(%rbp)
movq -0x30(%rbp), %rax
movq %rax, -0xc0(%rbp)
movq -0x30(%rbp), %rax
movslq -0x94(%rbp), %rcx
xorl %edx, %edx
divq %rcx
movq -0xc0(%rbp), %rax
subq %rdx, %rax
movq %rax, -0xa0(%rbp)
movl $0x0, -0xa4(%rbp)
movl -0xa4(%rbp), %eax
movl %eax, -0xc4(%rbp)
movl -0x94(%rbp), %eax
movl $0x2, %ecx
cltd
idivl %ecx
movl %eax, %ecx
movl -0xc4(%rbp), %eax
cmpl %ecx, %eax
jge 0x1c26a
movq -0xb0(%rbp), %rax
movq -0x68(%rbp), %rcx
movq 0x8(%rcx), %rcx
movq %rcx, -0xd8(%rbp)
movq 0x8(%rax), %rax
movq (%rax), %rax
movq %rax, -0xd0(%rbp)
movq -0xa0(%rbp), %rcx
movl -0x94(%rbp), %eax
movl $0x2, %esi
cltd
idivl %esi
movq -0xd8(%rbp), %rdx
movl %eax, %esi
movq -0xd0(%rbp), %rax
movslq %esi, %rsi
addq %rsi, %rcx
movslq -0xa4(%rbp), %rsi
addq %rsi, %rcx
movq %rdx, (%rax,%rcx,8)
movl -0xa4(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xa4(%rbp)
jmp 0x1c1d3
movl $0x0, -0xa8(%rbp)
movl -0xa8(%rbp), %eax
movl %eax, -0xdc(%rbp)
movl -0x94(%rbp), %eax
movl $0x2, %ecx
cltd
idivl %ecx
movl %eax, %ecx
movl -0xdc(%rbp), %eax
cmpl %ecx, %eax
jge 0x1c2d5
movq -0xb0(%rbp), %rax
movq -0x68(%rbp), %rcx
movq (%rcx), %rdx
movq 0x8(%rax), %rax
movq (%rax), %rax
movq -0xa0(%rbp), %rcx
movslq -0xa8(%rbp), %rsi
addq %rsi, %rcx
movq %rdx, (%rax,%rcx,8)
movl -0xa8(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xa8(%rbp)
jmp 0x1c274
movq -0xb0(%rbp), %rax
movq 0x8(%rax), %rdi
callq 0x1d010
jmp 0x1c2e7
movl $0x1, %edi
leaq 0x3e98d(%rip), %rsi     # 0x5ac80
callq 0x63d0
movq 0x3e989(%rip), %rax     # 0x5ac88
subq 0x3e972(%rip), %rax     # 0x5ac78
movq 0x3e973(%rip), %rcx     # 0x5ac80
subq 0x3e95c(%rip), %rcx     # 0x5ac70
imulq $0x3b9aca00, %rcx, %rcx # imm = 0x3B9ACA00
addq %rcx, %rax
addq 0x3e93b(%rip), %rax     # 0x5ac60
movq %rax, 0x3e934(%rip)     # 0x5ac60
callq 0x1cbf0
jmp 0x1bcff
movq -0x88(%rbp), %rdi
callq 0x6780
nopw %cs:(%rax,%rax)
nopl (%rax)