// File: /workspace/github_c_makefile_star2_O3/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
movl $0x1, %edi
movl $0xa200, %esi           # imm = 0xA200
callq 0x10b0
testq %rax, %rax
je 0xeb1f
movq %rax, %rbx
movb %bpl, 0x20(%rax)
movq %r15, 0x7ca0(%rax)
movq %r12, 0x7ca8(%rax)
movw $0xffff, 0x24(%rax)     # imm = 0xFFFF
movq $-0x1, 0x3c(%rax)
vpcmpeqd %xmm0, %xmm0, %xmm0
vmovdqa %xmm0, 0x1790(%rax)
movq 0x306e1(%rip), %rax     # 0x3efb8
movq %rax, 0x180(%rbx)
movq 0x306b3(%rip), %rax     # 0x3ef98
movq %rax, 0x2b0(%rbx)
movq %rax, 0x290(%rbx)
leaq 0x239(%rip), %rax       # 0xeb33
movq %rax, 0x2e8(%rbx)
movq %rax, 0x2e0(%rbx)
movq %rax, 0x2d8(%rbx)
movq %rax, 0x2b8(%rbx)
movq 0x306a3(%rip), %rax     # 0x3efc0
movq %rax, 0x300(%rbx)
movq %rax, 0x2c0(%rbx)
movq 0x3067e(%rip), %rax     # 0x3efb0
movq %rax, 0x2c8(%rbx)
movq 0x306a8(%rip), %rax     # 0x3efe8
movq %rax, 0x2d0(%rbx)
movq 0x30682(%rip), %rax     # 0x3efd0
movq %rax, 0x328(%rbx)
movq %rax, 0x2f8(%rbx)
testq %r15, %r15
je 0xe9dd
movq 0x30640(%rip), %rax     # 0x3efa8
movq %rax, 0x2b0(%rbx)
movq %rax, 0x290(%rbx)
movq 0x30603(%rip), %rax     # 0x3ef80
movq %rax, 0x2b8(%rbx)
movq 0x305fd(%rip), %rax     # 0x3ef88
movq %rax, 0x300(%rbx)
movq %rax, 0x2c0(%rbx)
movq 0x30628(%rip), %rax     # 0x3efc8
movq %rax, 0x2c8(%rbx)
movq 0x305c2(%rip), %rax     # 0x3ef70
movq %rax, 0x2d0(%rbx)
movq 0x3061c(%rip), %rax     # 0x3efd8
movq %rax, 0x328(%rbx)
movq %rax, 0x2f8(%rbx)
testl %r14d, %r14d
je 0xe9dd
movq 0x305ca(%rip), %rax     # 0x3efa0
movq %rax, 0x180(%rbx)
testl %ebp, %ebp
jns 0xe9f6
movl $0x54, %edi
callq 0x1120
cmpl $0x10, %eax
movl $0x10, %ebp
cmovll %eax, %ebp
testl %ebp, %ebp
je 0xeb21
leaq 0x1590(%rbx), %r14
movq %r14, %rdi
xorl %esi, %esi
callq 0x1140
testl %eax, %eax
jne 0xeb17
leaq 0x15b8(%rbx), %r15
movq %r15, %rdi
xorl %esi, %esi
callq 0x10d0
testl %eax, %eax
jne 0xeb0f
leaq 0x15e8(%rbx), %r12
movq %r12, %rdi
xorl %esi, %esi
callq 0x10d0
testl %eax, %eax
jne 0xeb07
leaq 0x1618(%rbx), %r13
movq %r13, %rdi
xorl %esi, %esi
callq 0x10d0
testl %eax, %eax
jne 0xeaff
testl %ebp, %ebp
jle 0xeaf7
movq %r13, 0x8(%rsp)
movq %r12, 0x10(%rsp)
movq %rbx, %r13
addq $0x1510, %r13           # imm = 0x1510
movl %ebp, %r12d
movq $-0x2a1, %rbp           # imm = 0xFD5F
movq 0x180(%rbx), %rdx
movq %r13, %rdi
xorl %esi, %esi
movq %rbx, %rcx
callq 0x1100
testl %eax, %eax
jne 0xeab8
addq $0x8, %r13
leaq (%r12,%rbp), %rax
decq %rax
decq %rbp
cmpq $-0x2a1, %rax           # imm = 0xFD5F
jne 0xea88
jmp 0xeb21
addl %ebp, %r12d
cmpl $0xfffffd5f, %r12d      # imm = 0xFFFFFD5F
je 0xeb21
cmpq $-0x2a1, %rbp           # imm = 0xFD5F
movq 0x10(%rsp), %r12
movq 0x8(%rsp), %r13
je 0xeaf7
negq %rbp
movq (%rbx,%rbp,8), %rdi
callq 0x1130
leaq -0x1(%rbp), %rax
addq $-0x2a1, %rbp           # imm = 0xFD5F
cmpq $0x1, %rbp
movq %rax, %rbp
ja 0xeada
movq %r13, %rdi
callq 0x1110
movq %r12, %rdi
callq 0x1110
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