// File: Alcaro[P]bridges/arlib/deflate-decomp.cpp
// Function: unpack_huffman_dfl(unsigned short*, unsigned char const*, unsigned long, unsigned long)
static bool unpack_huffman_dfl(uint16_t * out, const uint8_t * in, size_t in_len, size_t bad_symbol)
{
	uint16_t n_len[16] = {};
	for (size_t n=0;n<in_len;n++)
		n_len[in[n]]++;
	
	size_t used_bits = 0;
	uint16_t bits_start[16];
	for (size_t n=1;n<16;n++)
	{
		bits_start[n] = used_bits;
		used_bits += (0x10000>>n) * n_len[n];
	}
	
	// if everything defined as accepted by RFC 1951 is a valid DEFLATE bytestram and everything else is forbidden,
	//  then a huffman table containing two or more zeroes and nothing else is illegal
	//  "One distance code of zero bits means that there are no distance codes used at all (the data is all literals)."
	// if everything with a reasonable and unambiguous definition in 1951 is legal, then all incomplete huffman tables are legal,
	//  if the unused bit sequences never show up
	// zlib matches neither behavior; it accepts a huffman table containing multiple zeroes and nothing else,
	//  as well as one 1-bit symbol and the rest zeroes, but anything else is rejected
	// this one matches zlib
	// https://github.com/madler/zlib/blob/cacf7f1d4e3d44d871b605da3b647f07d718623f/inftrees.c#L130-L138
	// https://tools.ietf.org/html/rfc1951#page-13
	if (UNLIKELY(used_bits != 0x10000))
	{
		if (used_bits > 0x10000) return false; // no overfull tables
		if (used_bits != (size_t)(n_len[1]<<15)) return false; // only 1-bit and unrepresentable symbols
		if (bad_symbol == 19) return false; // huffman huffman table can't be incomplete
		// (zlib implements this check differently, but our results are the same)
		assert_reached();
	}
	// used_bits can be 0, 0x8000, or 0x10000
	
	size_t out_tree = 1<<HUFF_FAST_BITS;
	memset(out, 0xFF, sizeof(uint16_t)<<HUFF_FAST_BITS);
	
	for (size_t n=0;n<in_len;n++)
	{
		int n_bits = in[n];
		if (!n_bits) continue;
		uint16_t bits = bitreverse16(bits_start[n_bits]);
		bits_start[n_bits] += 0x10000 >> n_bits;
		
		uint32_t layer_start = 0;
		int layer_bits = HUFF_FAST_BITS;
		int layer_bits_mask = (1<<layer_bits)-1;
		int bits_used = 0;
		
		while (n_bits > layer_bits)
		{
			uint16_t& link = out[layer_start | (bits&layer_bits_mask)];
			
			bits >>= layer_bits;
			n_bits -= layer_bits;
			bits_used += layer_bits;
			
			if (link == 0xFFFF)
			{
				// create a new layer in the huffman table
#ifdef ARLIB_TEST
				assert(out_tree < inflator::huff_table_size_286);
#endif
				link = 0x8000 | (bits_used << 11) | out_tree;
				layer_start = out_tree;
				memset(out+out_tree, 0xFF, sizeof(uint16_t)<<HUFF_SLOW_BITS);
				out_tree += 1<<HUFF_SLOW_BITS;
			}
			else
			{
				// follow the existing node
#ifdef ARLIB_TEST
				assert(link & 0x8000);
#endif
				layer_start = link&0x7FF;
			}
			
			layer_bits = HUFF_SLOW_BITS;
			layer_bits_mask = (1<<layer_bits)-1;
		}
		
		for (int clones=0;clones<(1<<(layer_bits-n_bits));clones++)
		{
			uint16_t& leaf = out[layer_start | (clones<<n_bits) | (bits & layer_bits_mask)];
#ifdef ARLIB_TEST
			assert(leaf == 0xFFFF);
			assert(n_bits > 0);
			assert_eq(bits_used+n_bits, in[n]);
#endif
			leaf = 0x0000 | ((bits_used+n_bits) << 11) | n;
			if (n == 256) leaf |= 128; // remap EOF, saves an instruction in the symbol decoder fast path
		}
	}
	
	if (UNLIKELY(used_bits != 0x10000))
	{
		// used_bits can be 0, 0x8000, or 0x10000, and can't be the last one in this branch
		// if 0, erase all; if 0x8000, erase only odd indices, up to out_tree
		// I can't find any solution better than the obvious one - just wipe all slots that are 0xFFFF
		for (size_t i=0;i<out_tree;i++)
		{
			if (out[i] == 0xFFFF)
				out[i] = 0x0000 | (0 << 11) | bad_symbol;
		}
	}
	
	return true;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0xe0, %rsp
movq %rdi, -0x10(%rbp)
movq %rsi, -0x18(%rbp)
movq %rdx, -0x20(%rbp)
movq %rcx, -0x28(%rbp)
leaq -0x50(%rbp), %rdi
xorl %esi, %esi
movl $0x20, %edx
callq 0x4170
movq $0x0, -0x58(%rbp)
movq -0x58(%rbp), %rax
cmpq -0x20(%rbp), %rax
jae 0xf7d5
movq -0x18(%rbp), %rax
movq -0x58(%rbp), %rcx
movzbl (%rax,%rcx), %eax
movw -0x50(%rbp,%rax,2), %cx
addw $0x1, %cx
movw %cx, -0x50(%rbp,%rax,2)
movq -0x58(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x58(%rbp)
jmp 0xf7a3
movq $0x0, -0x60(%rbp)
movq $0x1, -0x88(%rbp)
cmpq $0x10, -0x88(%rbp)
jae 0xf842
movq -0x60(%rbp), %rax
movw %ax, %cx
movq -0x88(%rbp), %rax
movw %cx, -0x80(%rbp,%rax,2)
movq -0x88(%rbp), %rax
movl %eax, %ecx
movl $0x10000, %eax          # imm = 0x10000
sarl %cl, %eax
movq -0x88(%rbp), %rcx
movzwl -0x50(%rbp,%rcx,2), %ecx
imull %ecx, %eax
cltq
addq -0x60(%rbp), %rax
movq %rax, -0x60(%rbp)
movq -0x88(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x88(%rbp)
jmp 0xf7e8
cmpq $0x10000, -0x60(%rbp)   # imm = 0x10000
setne %al
xorb $-0x1, %al
xorb $-0x1, %al
testb $0x1, %al
jne 0xf857
jmp 0xf898
cmpq $0x10000, -0x60(%rbp)   # imm = 0x10000
jbe 0xf86a
movb $0x0, -0x1(%rbp)
jmp 0xfbeb
movq -0x60(%rbp), %rax
movzwl -0x4e(%rbp), %ecx
shll $0xf, %ecx
movslq %ecx, %rcx
cmpq %rcx, %rax
je 0xf886
movb $0x0, -0x1(%rbp)
jmp 0xfbeb
cmpq $0x13, -0x28(%rbp)
jne 0xf896
movb $0x0, -0x1(%rbp)
jmp 0xfbeb
jmp 0xf898
movq $0x200, -0x90(%rbp) # imm = 0x200
movq -0x10(%rbp), %rdi
movl $0xff, %esi
movl $0x400, %edx            # imm = 0x400
callq 0x4170
movq $0x0, -0x98(%rbp)
movq -0x98(%rbp), %rax
cmpq -0x20(%rbp), %rax
jae 0xfb6f
movq -0x18(%rbp), %rax
movq -0x98(%rbp), %rcx
movzbl (%rax,%rcx), %eax
movl %eax, -0x9c(%rbp)
cmpl $0x0, -0x9c(%rbp)
jne 0xf8f5
jmp 0xfb58
movslq -0x9c(%rbp), %rax
movzwl -0x80(%rbp,%rax,2), %edi
callq 0xffe0
movw %ax, -0x9e(%rbp)
movl -0x9c(%rbp), %ecx
movl $0x10000, %edx          # imm = 0x10000
sarl %cl, %edx
movslq -0x9c(%rbp), %rax
movzwl -0x80(%rbp,%rax,2), %ecx
addl %edx, %ecx
movw %cx, -0x80(%rbp,%rax,2)
movl $0x0, -0xa4(%rbp)
movl $0x9, -0xa8(%rbp)
movl -0xa8(%rbp), %ecx
movl $0x1, %eax
shll %cl, %eax
subl $0x1, %eax
movl %eax, -0xac(%rbp)
movl $0x0, -0xb0(%rbp)
movl -0x9c(%rbp), %eax
cmpl -0xa8(%rbp), %eax
jle 0xfa87
movq -0x10(%rbp), %rax
movl -0xa4(%rbp), %ecx
movzwl -0x9e(%rbp), %edx
andl -0xac(%rbp), %edx
orl %edx, %ecx
movl %ecx, %ecx
shlq %rcx
addq %rcx, %rax
movq %rax, -0xb8(%rbp)
movl -0xa8(%rbp), %ecx
movzwl -0x9e(%rbp), %eax
sarl %cl, %eax
movw %ax, -0x9e(%rbp)
movl -0xa8(%rbp), %ecx
movl -0x9c(%rbp), %eax
subl %ecx, %eax
movl %eax, -0x9c(%rbp)
movl -0xa8(%rbp), %eax
addl -0xb0(%rbp), %eax
movl %eax, -0xb0(%rbp)
movq -0xb8(%rbp), %rax
movzwl (%rax), %eax
cmpl $0xffff, %eax           # imm = 0xFFFF
jne 0xfa4d
movl -0xb0(%rbp), %eax
shll $0xb, %eax
orl $0x8000, %eax           # imm = 0x8000
cltq
orq -0x90(%rbp), %rax
movw %ax, %cx
movq -0xb8(%rbp), %rax
movw %cx, (%rax)
movq -0x90(%rbp), %rax
movl %eax, -0xa4(%rbp)
movq -0x10(%rbp), %rdi
movq -0x90(%rbp), %rax
shlq %rax
addq %rax, %rdi
movl $0xff, %esi
movl $0x10, %edx
callq 0x4170
movq -0x90(%rbp), %rax
addq $0x8, %rax
movq %rax, -0x90(%rbp)
jmp 0xfa62
movq -0xb8(%rbp), %rax
movzwl (%rax), %eax
andl $0x7ff, %eax            # imm = 0x7FF
movl %eax, -0xa4(%rbp)
movl $0x3, -0xa8(%rbp)
movl -0xa8(%rbp), %ecx
movl $0x1, %eax
shll %cl, %eax
subl $0x1, %eax
movl %eax, -0xac(%rbp)
jmp 0xf961
movl $0x0, -0xbc(%rbp)
movl -0xbc(%rbp), %eax
movl -0xa8(%rbp), %ecx
subl -0x9c(%rbp), %ecx
movl $0x1, %edx
shll %cl, %edx
movl %edx, %ecx
cmpl %ecx, %eax
jge 0xfb56
movq -0x10(%rbp), %rax
movl -0xa4(%rbp), %ecx
movl %ecx, -0xd4(%rbp)
movl -0xbc(%rbp), %edx
movl -0x9c(%rbp), %ecx
shll %cl, %edx
movl -0xd4(%rbp), %ecx
orl %edx, %ecx
movzwl -0x9e(%rbp), %edx
andl -0xac(%rbp), %edx
orl %edx, %ecx
movl %ecx, %ecx
shlq %rcx
addq %rcx, %rax
movq %rax, -0xc8(%rbp)
movl -0xb0(%rbp), %eax
addl -0x9c(%rbp), %eax
shll $0xb, %eax
orl $0x0, %eax
cltq
orq -0x98(%rbp), %rax
movw %ax, %cx
movq -0xc8(%rbp), %rax
movw %cx, (%rax)
cmpq $0x100, -0x98(%rbp) # imm = 0x100
jne 0xfb40
movq -0xc8(%rbp), %rax
movzwl (%rax), %ecx
orl $0x80, %ecx
movw %cx, (%rax)
jmp 0xfb42
movl -0xbc(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xbc(%rbp)
jmp 0xfa91
jmp 0xfb58
movq -0x98(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x98(%rbp)
jmp 0xf8c1
cmpq $0x10000, -0x60(%rbp)   # imm = 0x10000
setne %al
xorb $-0x1, %al
xorb $-0x1, %al
testb $0x1, %al
jne 0xfb84
jmp 0xfbe7
movq $0x0, -0xd0(%rbp)
movq -0xd0(%rbp), %rax
cmpq -0x90(%rbp), %rax
jae 0xfbe5
movq -0x10(%rbp), %rax
movq -0xd0(%rbp), %rcx
movzwl (%rax,%rcx,2), %eax
cmpl $0xffff, %eax           # imm = 0xFFFF
jne 0xfbcf
movq -0x28(%rbp), %rax
orq $0x0, %rax
movw %ax, %dx
movq -0x10(%rbp), %rax
movq -0xd0(%rbp), %rcx
movw %dx, (%rax,%rcx,2)
jmp 0xfbd1
movq -0xd0(%rbp), %rax
addq $0x1, %rax
movq %rax, -0xd0(%rbp)
jmp 0xfb8f
jmp 0xfbe7
movb $0x1, -0x1(%rbp)
movb -0x1(%rbp), %al
andb $0x1, %al
addq $0xe0, %rsp
popq %rbp
retq
nopl (%rax)