// File: WojciechMula[P]utf16utf8/src/pext.cpp
// Function: pext_convert_utf8_to_utf16(unsigned char const*, unsigned long, unsigned short*)
size_t pext_convert_utf8_to_utf16(const uint8_t* input, size_t size, uint16_t* output) {
    uint16_t* output_start = output;
    const uint8_t* end = input + size;
    const uint8_t* curr = input;
    while (curr + 4 < end)
    {
        if (int8_t(*curr) >= 0) {
            *output++ = *curr++;
            continue;
        }

        const auto& entry = pext_lookup[*curr];
        if (entry.utf8_length == 0)
            // wrong leading byte
            abort();

        uint32_t value;
        memcpy(&value, input, 4);
        value = __builtin_bswap32(value);

        if ((entry.utf8_bits_mask) == entry.utf8_bits_expected)
            // wrong encoding
            abort();

        value = _pext_u32(value, entry.pext_mask);

        if (value <= 0xffff) {
            *output++ = static_cast<uint16_t>(value);
        } else {
            value -= 0x10000;
            *output++ = uint16_t(0xd800 | ((value >> 10) & 0x03ff));
            *output++ = uint16_t(0xdc00 | (value & 0x03ff));
            return 2;
        }

        curr += entry.utf8_length;
    }
    
    // TODO process the tail

    return output - output_start;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x50, %rsp
movq %rdi, -0x18(%rbp)
movq %rsi, -0x20(%rbp)
movq %rdx, -0x28(%rbp)
movq -0x28(%rbp), %rax
movq %rax, -0x30(%rbp)
movq -0x18(%rbp), %rax
addq -0x20(%rbp), %rax
movq %rax, -0x38(%rbp)
movq -0x18(%rbp), %rax
movq %rax, -0x40(%rbp)
movq -0x40(%rbp), %rax
addq $0x4, %rax
cmpq -0x38(%rbp), %rax
jae 0xc5b1
movq -0x40(%rbp), %rax
movsbl (%rax), %eax
cmpl $0x0, %eax
jl 0xc4a7
movq -0x40(%rbp), %rax
movq %rax, %rcx
addq $0x1, %rcx
movq %rcx, -0x40(%rbp)
movzbl (%rax), %eax
movw %ax, %cx
movq -0x28(%rbp), %rax
movq %rax, %rdx
addq $0x2, %rdx
movq %rdx, -0x28(%rbp)
movw %cx, (%rax)
jmp 0xc460
movq -0x40(%rbp), %rax
movzbl (%rax), %eax
movl %eax, %ecx
leaq 0x15d09(%rip), %rax     # 0x221c0
shlq $0x4, %rcx
addq %rcx, %rax
movq %rax, -0x48(%rbp)
movq -0x48(%rbp), %rax
movzbl 0xc(%rax), %eax
cmpl $0x0, %eax
jne 0xc4d4
callq 0x2100
movq -0x18(%rbp), %rax
movl (%rax), %eax
movl %eax, -0x4c(%rbp)
movl -0x4c(%rbp), %eax
bswapl %eax
movl %eax, -0x4c(%rbp)
movq -0x48(%rbp), %rax
movl 0x4(%rax), %eax
movq -0x48(%rbp), %rcx
cmpl 0x8(%rcx), %eax
jne 0xc4fa
callq 0x2100
movl -0x4c(%rbp), %ecx
movq -0x48(%rbp), %rax
movl (%rax), %eax
movl %ecx, -0x4(%rbp)
movl %eax, -0x8(%rbp)
movl -0x4(%rbp), %eax
movl -0x8(%rbp), %ecx
pextl %ecx, %eax, %eax
movl %eax, -0x4c(%rbp)
cmpl $0xffff, -0x4c(%rbp)    # imm = 0xFFFF
ja 0xc53a
movl -0x4c(%rbp), %eax
movw %ax, %cx
movq -0x28(%rbp), %rax
movq %rax, %rdx
addq $0x2, %rdx
movq %rdx, -0x28(%rbp)
movw %cx, (%rax)
jmp 0xc596
movl -0x4c(%rbp), %eax
subl $0x10000, %eax          # imm = 0x10000
movl %eax, -0x4c(%rbp)
movl -0x4c(%rbp), %eax
shrl $0xa, %eax
andl $0x3ff, %eax            # imm = 0x3FF
orl $0xd800, %eax           # imm = 0xD800
movw %ax, %cx
movq -0x28(%rbp), %rax
movq %rax, %rdx
addq $0x2, %rdx
movq %rdx, -0x28(%rbp)
movw %cx, (%rax)
movl -0x4c(%rbp), %eax
andl $0x3ff, %eax            # imm = 0x3FF
orl $0xdc00, %eax           # imm = 0xDC00
movw %ax, %cx
movq -0x28(%rbp), %rax
movq %rax, %rdx
addq $0x2, %rdx
movq %rdx, -0x28(%rbp)
movw %cx, (%rax)
movq $0x2, -0x10(%rbp)
jmp 0xc5c3
movq -0x48(%rbp), %rax
movzbl 0xc(%rax), %ecx
movq -0x40(%rbp), %rax
movslq %ecx, %rcx
addq %rcx, %rax
movq %rax, -0x40(%rbp)
jmp 0xc460
movq -0x28(%rbp), %rax
movq -0x30(%rbp), %rcx
subq %rcx, %rax
sarq %rax
movq %rax, -0x10(%rbp)
movq -0x10(%rbp), %rax
addq $0x50, %rsp
popq %rbp
retq
nopl (%rax)