// File: /workspace/github_c_makefile_star2_O2/tvlabs[P]edge264/edge264.c
// Function: edge264_alloc
Edge264Decoder *edge264_alloc(int n_threads, void (*log_cb)(const char *str, void *log_arg), void *log_arg, int log_mbs) {
	Edge264Decoder *dec = calloc(1, sizeof(Edge264Decoder));
	if (dec == NULL)
		return NULL;
	dec->n_threads = n_threads;
	dec->log_cb = log_cb;
	dec->log_arg = log_arg;
	dec->currPic = dec->prevPic = -1;
	dec->PrevRefFrameNum[0] = dec->PrevRefFrameNum[1] = -1;
	dec->taskPics_v = set8(-1);
	
	// select parser functions based on CPU capabilities and logs mode
	dec->worker_loop = ADD_VARIANT(worker_loop);
	dec->parse_nal_unit[1] = dec->parse_nal_unit[5] = ADD_VARIANT(parse_slice_layer_without_partitioning);
	dec->parse_nal_unit[6] = dec->parse_nal_unit[10] = dec->parse_nal_unit[11] = dec->parse_nal_unit[12] = ignore_NAL;
	dec->parse_nal_unit[7] = dec->parse_nal_unit[15] = ADD_VARIANT(parse_seq_parameter_set);
	dec->parse_nal_unit[8] = ADD_VARIANT(parse_pic_parameter_set);
	dec->parse_nal_unit[9] = ADD_VARIANT(parse_access_unit_delimiter);
	dec->parse_nal_unit[14] = dec->parse_nal_unit[20] = ADD_VARIANT(parse_nal_unit_header_extension);
	#if defined(HAS_X86_64_V2) || defined(HAS_X86_64_V3)
		__builtin_cpu_init();
	#endif
	#ifdef HAS_X86_64_V2
		// macOS's clang does not support x86-64-v3/v2 feature string yet
		if (__builtin_cpu_supports("popcnt") &&
			__builtin_cpu_supports("sse3") &&
			__builtin_cpu_supports("sse4.1") &&
			__builtin_cpu_supports("sse4.2") &&
			__builtin_cpu_supports("ssse3")) {
			dec->worker_loop = worker_loop_v2;
			dec->parse_nal_unit[1] = dec->parse_nal_unit[5] = parse_slice_layer_without_partitioning_v2;
			dec->parse_nal_unit[7] = dec->parse_nal_unit[15] = parse_seq_parameter_set_v2;
			dec->parse_nal_unit[8] = parse_pic_parameter_set_v2;
			dec->parse_nal_unit[9] = parse_access_unit_delimiter_v2;
			dec->parse_nal_unit[14] = dec->parse_nal_unit[20] = parse_nal_unit_header_extension_v2;
		}
	#endif
	#ifdef HAS_X86_64_V3
		if (__builtin_cpu_supports("avx") &&
			__builtin_cpu_supports("avx2") &&
			__builtin_cpu_supports("bmi") &&
			__builtin_cpu_supports("bmi2") &&
			__builtin_cpu_supports("fma")) {
			dec->worker_loop = worker_loop_v3;
			dec->parse_nal_unit[1] = dec->parse_nal_unit[5] = parse_slice_layer_without_partitioning_v3;
			dec->parse_nal_unit[7] = dec->parse_nal_unit[15] = parse_seq_parameter_set_v3;
			dec->parse_nal_unit[8] = parse_pic_parameter_set_v3;
			dec->parse_nal_unit[9] = parse_access_unit_delimiter_v3;
			dec->parse_nal_unit[14] = dec->parse_nal_unit[20] = parse_nal_unit_header_extension_v3;
		}
	#endif
	#ifdef HAS_LOGS
		if (log_cb) {
			dec->parse_nal_unit[1] = dec->parse_nal_unit[5] = parse_slice_layer_without_partitioning_log;
			dec->parse_nal_unit[6] = parse_sei_log;
			dec->parse_nal_unit[7] = dec->parse_nal_unit[15] = parse_seq_parameter_set_log;
			dec->parse_nal_unit[8] = parse_pic_parameter_set_log;
			dec->parse_nal_unit[9] = parse_access_unit_delimiter_log;
			dec->parse_nal_unit[14] = dec->parse_nal_unit[20] = parse_nal_unit_header_extension_log;
			if (log_mbs)
				dec->worker_loop = worker_loop_log;
		}
	#else
		if (log_cb)
			return free(dec), NULL;
	#endif
	
	// get the number of logical cores if requested
	if (n_threads < 0) {
		#ifdef _WIN32
			int n_cpus = atoi(getenv("NUMBER_OF_PROCESSORS"));
		#else
			int n_cpus = sysconf(_SC_NPROCESSORS_ONLN);
		#endif
		n_threads = min(n_cpus, 16);
	}
	
	// if multithreading is disabled we are done, otherwise initialize all
	if (n_threads == 0)
		return dec;
	if (pthread_mutex_init(&dec->lock, NULL) == 0) {
		if (pthread_cond_init(&dec->task_ready, NULL) == 0) {
			if (pthread_cond_init(&dec->task_progress, NULL) == 0) {
				if (pthread_cond_init(&dec->task_complete, NULL) == 0) {
					int i = 0;
					while (i < n_threads && pthread_create(&dec->threads[i], NULL, (void*(*)(void*))dec->worker_loop, dec) == 0)
						i++;
					if (i == n_threads) {
						return dec;
					}
					while (i-- > 0)
						pthread_cancel(dec->threads[i]);
					pthread_cond_destroy(&dec->task_complete);
				}
				pthread_cond_destroy(&dec->task_progress);
			}
			pthread_cond_destroy(&dec->task_ready);
		}
		pthread_mutex_destroy(&dec->lock);
	}
	free(dec);
	return NULL;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x18, %rsp
movl %ecx, %r14d
movq %rdx, %r12
movq %rsi, %r15
movl %edi, %ebp
pushq $0x1
popq %rdi
movl $0xa200, %esi           # imm = 0xA200
callq 0x10b0
testq %rax, %rax
je 0xb430
movq %rax, %rbx
movb %bpl, 0x20(%rax)
movq %r15, 0x7ca0(%rax)
movq %r12, 0x7ca8(%rax)
orw $-0x1, 0x24(%rax)
orq $-0x1, 0x3c(%rax)
vpcmpeqd %xmm0, %xmm0, %xmm0
vmovdqa %xmm0, 0x1790(%rax)
movq 0x2edae(%rip), %rax     # 0x39fb8
movq %rax, 0x180(%rbx)
movq 0x2ed80(%rip), %rax     # 0x39f98
movq %rax, 0x2b0(%rbx)
movq %rax, 0x290(%rbx)
leaq 0x217(%rip), %rax       # 0xb444
movq %rax, 0x2e8(%rbx)
movq %rax, 0x2e0(%rbx)
movq %rax, 0x2d8(%rbx)
movq %rax, 0x2b8(%rbx)
movq 0x2ed70(%rip), %rax     # 0x39fc0
movq %rax, 0x300(%rbx)
movq %rax, 0x2c0(%rbx)
movq 0x2ed4b(%rip), %rax     # 0x39fb0
movq %rax, 0x2c8(%rbx)
movq 0x2ed75(%rip), %rax     # 0x39fe8
movq %rax, 0x2d0(%rbx)
movq 0x2ed4f(%rip), %rax     # 0x39fd0
movq %rax, 0x328(%rbx)
movq %rax, 0x2f8(%rbx)
testq %r15, %r15
je 0xb310
movq 0x2ed0d(%rip), %rax     # 0x39fa8
movq %rax, 0x2b0(%rbx)
movq %rax, 0x290(%rbx)
movq 0x2ecd0(%rip), %rax     # 0x39f80
movq %rax, 0x2b8(%rbx)
movq 0x2ecca(%rip), %rax     # 0x39f88
movq %rax, 0x300(%rbx)
movq %rax, 0x2c0(%rbx)
movq 0x2ecf5(%rip), %rax     # 0x39fc8
movq %rax, 0x2c8(%rbx)
movq 0x2ec8f(%rip), %rax     # 0x39f70
movq %rax, 0x2d0(%rbx)
movq 0x2ece9(%rip), %rax     # 0x39fd8
movq %rax, 0x328(%rbx)
movq %rax, 0x2f8(%rbx)
testl %r14d, %r14d
je 0xb310
movq 0x2ec97(%rip), %rax     # 0x39fa0
movq %rax, 0x180(%rbx)
testl %ebp, %ebp
jns 0xb325
pushq $0x54
popq %rdi
callq 0x1120
cmpl $0x10, %eax
pushq $0x10
popq %rbp
cmovll %eax, %ebp
testl %ebp, %ebp
je 0xb432
leaq 0x1590(%rbx), %r14
movq %r14, %rdi
xorl %esi, %esi
callq 0x1140
testl %eax, %eax
jne 0xb428
leaq 0x15b8(%rbx), %r15
movq %r15, %rdi
xorl %esi, %esi
callq 0x10d0
testl %eax, %eax
jne 0xb420
leaq 0x15e8(%rbx), %r12
movq %r12, %rdi
xorl %esi, %esi
callq 0x10d0
testl %eax, %eax
jne 0xb418
movq %r15, 0x10(%rsp)
leaq 0x1618(%rbx), %r13
movq %r13, %rdi
xorl %esi, %esi
callq 0x10d0
testl %eax, %eax
jne 0xb40b
movq %r13, 0x8(%rsp)
movq %r12, (%rsp)
movq %rbx, %r13
addq $0x1510, %r13           # imm = 0x1510
movl %ebp, %eax
sarl $0x1f, %eax
andnl %ebp, %eax, %r15d
xorl %r12d, %r12d
cmpq %r12, %r15
je 0xb3db
movq 0x180(%rbx), %rdx
movq %r13, %rdi
xorl %esi, %esi
movq %rbx, %rcx
callq 0x1100
testl %eax, %eax
jne 0xb3d8
incq %r12
addq $0x8, %r13
jmp 0xb3b2
movl %r12d, %r15d
cmpl %ebp, %r15d
movq 0x8(%rsp), %r13
je 0xb432
movl %r15d, %r12d
testq %r12, %r12
jle 0xb3ff
movq 0x1508(%rbx,%r12,8), %rdi
decq %r12
callq 0x1130
jmp 0xb3e8
movq %r13, %rdi
callq 0x1110
movq (%rsp), %r12
movq %r12, %rdi
callq 0x1110
movq 0x10(%rsp), %r15
movq %r15, %rdi
callq 0x1110
movq %r14, %rdi
callq 0x1060
movq %rbx, %rdi
callq 0x1030
xorl %ebx, %ebx
movq %rbx, %rax
addq $0x18, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq