// File: xocoder[P]m_kcptun/vendor/cm256/gf256.cpp
// Function: gf256_mul_mem
void gf256_mul_mem(void * GF256_RESTRICT vz, const void * GF256_RESTRICT vx, uint8_t y, int bytes)
{
    // Use a single if-statement to handle special cases
    if (y <= 1)
    {
        if (y == 0)
            memset(vz, 0, bytes);
        else if (vz != vx)
            memcpy(vz, vx, bytes);
        return;
    }

    GF256_M128 * GF256_RESTRICT z16 = reinterpret_cast<GF256_M128 *>(vz);
    const GF256_M128 * GF256_RESTRICT x16 = reinterpret_cast<const GF256_M128 *>(vx);

#if defined(GF256_TARGET_MOBILE)
#if defined(GF256_TRY_NEON)
    if (bytes >= 16 && CpuHasNeon)
    {
        // Partial product tables; see above
        const GF256_M128 table_lo_y = vld1q_u8((uint8_t*)(GF256Ctx.MM128.TABLE_LO_Y + y));
        const GF256_M128 table_hi_y = vld1q_u8((uint8_t*)(GF256Ctx.MM128.TABLE_HI_Y + y));

        // clr_mask = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f
        const GF256_M128 clr_mask = vdupq_n_u8(0x0f);

        // Handle multiples of 16 bytes
        do
        {
            // See above comments for details
            GF256_M128 x0 = vld1q_u8((uint8_t*)x16);
            GF256_M128 l0 = vandq_u8(x0, clr_mask);
            x0 = vshrq_n_u8(x0, 4);
            GF256_M128 h0 = vandq_u8(x0, clr_mask);
            l0 = vqtbl1q_u8(table_lo_y, l0);
            h0 = vqtbl1q_u8(table_hi_y, h0);
            vst1q_u8((uint8_t*)z16, veorq_u8(l0, h0));

            bytes -= 16, ++x16, ++z16;
        } while (bytes >= 16);
    }
#endif
#else
# if defined(GF256_TRY_AVX2)
    if (bytes >= 32 && CpuHasAVX2)
    {
        // Partial product tables; see above
        const GF256_M256 table_lo_y = _mm256_loadu_si256(GF256Ctx.MM256.TABLE_LO_Y + y);
        const GF256_M256 table_hi_y = _mm256_loadu_si256(GF256Ctx.MM256.TABLE_HI_Y + y);

        // clr_mask = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f
        const GF256_M256 clr_mask = _mm256_set1_epi8(0x0f);

        GF256_M256 * GF256_RESTRICT z32 = reinterpret_cast<GF256_M256 *>(vz);
        const GF256_M256 * GF256_RESTRICT x32 = reinterpret_cast<const GF256_M256 *>(vx);

        // Handle multiples of 32 bytes
        do
        {
            // See above comments for details
            GF256_M256 x0 = _mm256_loadu_si256(x32);
            GF256_M256 l0 = _mm256_and_si256(x0, clr_mask);
            x0 = _mm256_srli_epi64(x0, 4);
            GF256_M256 h0 = _mm256_and_si256(x0, clr_mask);
            l0 = _mm256_shuffle_epi8(table_lo_y, l0);
            h0 = _mm256_shuffle_epi8(table_hi_y, h0);
            _mm256_storeu_si256(z32, _mm256_xor_si256(l0, h0));

            bytes -= 32, ++x32, ++z32;
        } while (bytes >= 32);

        z16 = reinterpret_cast<GF256_M128 *>(z32);
        x16 = reinterpret_cast<const GF256_M128 *>(x32);
    }
# endif // GF256_TRY_AVX2
    if (bytes >= 16 && CpuHasSSSE3)
    {
        // Partial product tables; see above
        const GF256_M128 table_lo_y = _mm_loadu_si128(GF256Ctx.MM128.TABLE_LO_Y + y);
        const GF256_M128 table_hi_y = _mm_loadu_si128(GF256Ctx.MM128.TABLE_HI_Y + y);

        // clr_mask = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f
        const GF256_M128 clr_mask = _mm_set1_epi8(0x0f);

        // Handle multiples of 16 bytes
        do
        {
            // See above comments for details
            GF256_M128 x0 = _mm_loadu_si128(x16);
            GF256_M128 l0 = _mm_and_si128(x0, clr_mask);
            x0 = _mm_srli_epi64(x0, 4);
            GF256_M128 h0 = _mm_and_si128(x0, clr_mask);
            l0 = _mm_shuffle_epi8(table_lo_y, l0);
            h0 = _mm_shuffle_epi8(table_hi_y, h0);
            _mm_storeu_si128(z16, _mm_xor_si128(l0, h0));

            bytes -= 16, ++x16, ++z16;
        } while (bytes >= 16);
    }
#endif

    uint8_t * GF256_RESTRICT z1 = reinterpret_cast<uint8_t*>(z16);
    const uint8_t * GF256_RESTRICT x1 = reinterpret_cast<const uint8_t*>(x16);
    const uint8_t * GF256_RESTRICT table = GF256Ctx.GF256_MUL_TABLE + ((unsigned)y << 8);

    // Handle blocks of 8 bytes
    while (bytes >= 8)
    {
        uint64_t * GF256_RESTRICT z8 = reinterpret_cast<uint64_t *>(z1);
        uint64_t word = table[x1[0]];
        word |= (uint64_t)table[x1[1]] << 8;
        word |= (uint64_t)table[x1[2]] << 16;
        word |= (uint64_t)table[x1[3]] << 24;
        word |= (uint64_t)table[x1[4]] << 32;
        word |= (uint64_t)table[x1[5]] << 40;
        word |= (uint64_t)table[x1[6]] << 48;
        word |= (uint64_t)table[x1[7]] << 56;
        *z8 = word;

        bytes -= 8, x1 += 8, z1 += 8;
    }

    // Handle a block of 4 bytes
    const int four = bytes & 4;
    if (four)
    {
        uint32_t * GF256_RESTRICT z4 = reinterpret_cast<uint32_t *>(z1);
        uint32_t word = table[x1[0]];
        word |= (uint32_t)table[x1[1]] << 8;
        word |= (uint32_t)table[x1[2]] << 16;
        word |= (uint32_t)table[x1[3]] << 24;
        *z4 = word;
    }

    // Handle single bytes
    const int offset = four;
    switch (bytes & 3)
    {
    case 3: z1[offset + 2] = table[x1[offset + 2]];
    case 2: z1[offset + 1] = table[x1[offset + 1]];
    case 1: z1[offset] = table[x1[offset]];
    default:
        break;
    }
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x1f0, %rsp            # imm = 0x1F0
movb %dl, %al
movq %rdi, -0x120(%rbp)
movq %rsi, -0x128(%rbp)
movb %al, -0x129(%rbp)
movl %ecx, -0x130(%rbp)
movzbl -0x129(%rbp), %eax
cmpl $0x1, %eax
jg 0x11917
movzbl -0x129(%rbp), %eax
cmpl $0x0, %eax
jne 0x118e6
movq -0x120(%rbp), %rdi
movslq -0x130(%rbp), %rdx
xorl %esi, %esi
callq 0x31c0
jmp 0x11912
movq -0x120(%rbp), %rax
cmpq -0x128(%rbp), %rax
je 0x11910
movq -0x120(%rbp), %rdi
movq -0x128(%rbp), %rsi
movslq -0x130(%rbp), %rdx
callq 0x3290
jmp 0x11912
jmp 0x11fd2
movq -0x120(%rbp), %rax
movq %rax, -0x138(%rbp)
movq -0x128(%rbp), %rax
movq %rax, -0x140(%rbp)
cmpl $0x10, -0x130(%rbp)
jl 0x11c55
testb $0x1, 0x2925a(%rip)     # 0x3aba1
je 0x11c55
movzbl -0x129(%rbp), %eax
shll $0x4, %eax
movl %eax, %edx
leaq 0x6a30(%rip), %rcx      # 0x18390
movq %rcx, %rax
addq %rdx, %rax
movq %rax, -0x108(%rbp)
movq -0x108(%rbp), %rax
movups (%rax), %xmm0
movaps %xmm0, -0x150(%rbp)
movzbl -0x129(%rbp), %eax
shll $0x4, %eax
leaq 0x1000(%rax,%rcx), %rax
movq %rax, -0x110(%rbp)
movq -0x110(%rbp), %rax
movups (%rax), %xmm0
movaps %xmm0, -0x160(%rbp)
movb $0xf, -0xc1(%rbp)
movb -0xc1(%rbp), %al
movb %al, -0x1e1(%rbp)
movb %al, -0x1(%rbp)
movb %al, -0x2(%rbp)
movb %al, -0x3(%rbp)
movb %al, -0x4(%rbp)
movb %al, -0x5(%rbp)
movb %al, -0x6(%rbp)
movb %al, -0x7(%rbp)
movb %al, -0x8(%rbp)
movb %al, -0x9(%rbp)
movb %al, -0xa(%rbp)
movb %al, -0xb(%rbp)
movb %al, -0xc(%rbp)
movb %al, -0xd(%rbp)
movb %al, -0xe(%rbp)
movb %al, -0xf(%rbp)
movb %al, -0x10(%rbp)
movzbl -0x10(%rbp), %eax
movd %eax, %xmm0
movzbl -0xf(%rbp), %eax
pinsrb $0x1, %eax, %xmm0
movzbl -0xe(%rbp), %eax
pinsrb $0x2, %eax, %xmm0
movzbl -0xd(%rbp), %eax
pinsrb $0x3, %eax, %xmm0
movzbl -0xc(%rbp), %eax
pinsrb $0x4, %eax, %xmm0
movzbl -0xb(%rbp), %eax
pinsrb $0x5, %eax, %xmm0
movzbl -0xa(%rbp), %eax
pinsrb $0x6, %eax, %xmm0
movzbl -0x9(%rbp), %eax
pinsrb $0x7, %eax, %xmm0
movzbl -0x8(%rbp), %eax
pinsrb $0x8, %eax, %xmm0
movzbl -0x7(%rbp), %eax
pinsrb $0x9, %eax, %xmm0
movzbl -0x6(%rbp), %eax
pinsrb $0xa, %eax, %xmm0
movzbl -0x5(%rbp), %eax
pinsrb $0xb, %eax, %xmm0
movzbl -0x4(%rbp), %eax
pinsrb $0xc, %eax, %xmm0
movzbl -0x3(%rbp), %eax
pinsrb $0xd, %eax, %xmm0
movzbl -0x2(%rbp), %eax
pinsrb $0xe, %eax, %xmm0
movzbl -0x1(%rbp), %eax
pinsrb $0xf, %eax, %xmm0
movdqa %xmm0, -0x20(%rbp)
movdqa -0x20(%rbp), %xmm0
movdqa %xmm0, -0x170(%rbp)
movq -0x140(%rbp), %rax
movq %rax, -0x118(%rbp)
movq -0x118(%rbp), %rax
movdqu (%rax), %xmm0
movdqa %xmm0, -0x180(%rbp)
movdqa -0x180(%rbp), %xmm1
movdqa -0x170(%rbp), %xmm0
movdqa %xmm1, -0x90(%rbp)
movdqa %xmm0, -0xa0(%rbp)
movdqa -0x90(%rbp), %xmm0
pand -0xa0(%rbp), %xmm0
movdqa %xmm0, -0x190(%rbp)
movdqa -0x180(%rbp), %xmm0
movdqa %xmm0, -0x70(%rbp)
movl $0x4, -0x74(%rbp)
movdqa -0x70(%rbp), %xmm0
movl -0x74(%rbp), %eax
movd %eax, %xmm1
psrlq %xmm1, %xmm0
movdqa %xmm0, -0x180(%rbp)
movdqa -0x180(%rbp), %xmm1
movdqa -0x170(%rbp), %xmm0
movdqa %xmm1, -0xb0(%rbp)
movdqa %xmm0, -0xc0(%rbp)
movdqa -0xb0(%rbp), %xmm0
pand -0xc0(%rbp), %xmm0
movdqa %xmm0, -0x1a0(%rbp)
movdqa -0x150(%rbp), %xmm1
movdqa -0x190(%rbp), %xmm0
movdqa %xmm1, -0x30(%rbp)
movdqa %xmm0, -0x40(%rbp)
movdqa -0x30(%rbp), %xmm0
movdqa -0x40(%rbp), %xmm1
pshufb %xmm1, %xmm0
movdqa %xmm0, -0x190(%rbp)
movdqa -0x160(%rbp), %xmm1
movdqa -0x1a0(%rbp), %xmm0
movdqa %xmm1, -0x50(%rbp)
movdqa %xmm0, -0x60(%rbp)
movdqa -0x50(%rbp), %xmm0
movdqa -0x60(%rbp), %xmm1
pshufb %xmm1, %xmm0
movdqa %xmm0, -0x1a0(%rbp)
movq -0x138(%rbp), %rax
movdqa -0x190(%rbp), %xmm1
movdqa -0x1a0(%rbp), %xmm0
movdqa %xmm1, -0xf0(%rbp)
movdqa %xmm0, -0x100(%rbp)
movdqa -0xf0(%rbp), %xmm0
pxor -0x100(%rbp), %xmm0
movq %rax, -0xd0(%rbp)
movdqa %xmm0, -0xe0(%rbp)
movdqa -0xe0(%rbp), %xmm0
movq -0xd0(%rbp), %rax
movdqu %xmm0, (%rax)
movl -0x130(%rbp), %eax
subl $0x10, %eax
movl %eax, -0x130(%rbp)
movq -0x140(%rbp), %rax
addq $0x10, %rax
movq %rax, -0x140(%rbp)
movq -0x138(%rbp), %rax
addq $0x10, %rax
movq %rax, -0x138(%rbp)
cmpl $0x10, -0x130(%rbp)
jge 0x11a9b
jmp 0x11c55
movq -0x138(%rbp), %rax
movq %rax, -0x1a8(%rbp)
movq -0x140(%rbp), %rax
movq %rax, -0x1b0(%rbp)
movzbl -0x129(%rbp), %eax
shll $0x8, %eax
movl %eax, %eax
movl %eax, %ecx
leaq 0x670a(%rip), %rax      # 0x18390
addq $0x2000, %rax           # imm = 0x2000
addq %rcx, %rax
movq %rax, -0x1b8(%rbp)
cmpl $0x8, -0x130(%rbp)
jl 0x11e2e
movq -0x1a8(%rbp), %rax
movq %rax, -0x1c0(%rbp)
movq -0x1b8(%rbp), %rax
movq -0x1b0(%rbp), %rcx
movzbl (%rcx), %ecx
movzbl (%rax,%rcx), %eax
movq %rax, -0x1c8(%rbp)
movq -0x1b8(%rbp), %rax
movq -0x1b0(%rbp), %rcx
movzbl 0x1(%rcx), %ecx
movzbl (%rax,%rcx), %eax
shlq $0x8, %rax
orq -0x1c8(%rbp), %rax
movq %rax, -0x1c8(%rbp)
movq -0x1b8(%rbp), %rax
movq -0x1b0(%rbp), %rcx
movzbl 0x2(%rcx), %ecx
movzbl (%rax,%rcx), %eax
shlq $0x10, %rax
orq -0x1c8(%rbp), %rax
movq %rax, -0x1c8(%rbp)
movq -0x1b8(%rbp), %rax
movq -0x1b0(%rbp), %rcx
movzbl 0x3(%rcx), %ecx
movzbl (%rax,%rcx), %eax
shlq $0x18, %rax
orq -0x1c8(%rbp), %rax
movq %rax, -0x1c8(%rbp)
movq -0x1b8(%rbp), %rax
movq -0x1b0(%rbp), %rcx
movzbl 0x4(%rcx), %ecx
movzbl (%rax,%rcx), %eax
shlq $0x20, %rax
orq -0x1c8(%rbp), %rax
movq %rax, -0x1c8(%rbp)
movq -0x1b8(%rbp), %rax
movq -0x1b0(%rbp), %rcx
movzbl 0x5(%rcx), %ecx
movzbl (%rax,%rcx), %eax
shlq $0x28, %rax
orq -0x1c8(%rbp), %rax
movq %rax, -0x1c8(%rbp)
movq -0x1b8(%rbp), %rax
movq -0x1b0(%rbp), %rcx
movzbl 0x6(%rcx), %ecx
movzbl (%rax,%rcx), %eax
shlq $0x30, %rax
orq -0x1c8(%rbp), %rax
movq %rax, -0x1c8(%rbp)
movq -0x1b8(%rbp), %rax
movq -0x1b0(%rbp), %rcx
movzbl 0x7(%rcx), %ecx
movzbl (%rax,%rcx), %eax
shlq $0x38, %rax
orq -0x1c8(%rbp), %rax
movq %rax, -0x1c8(%rbp)
movq -0x1c8(%rbp), %rcx
movq -0x1c0(%rbp), %rax
movq %rcx, (%rax)
movl -0x130(%rbp), %eax
subl $0x8, %eax
movl %eax, -0x130(%rbp)
movq -0x1b0(%rbp), %rax
addq $0x8, %rax
movq %rax, -0x1b0(%rbp)
movq -0x1a8(%rbp), %rax
addq $0x8, %rax
movq %rax, -0x1a8(%rbp)
jmp 0x11c96
movl -0x130(%rbp), %eax
andl $0x4, %eax
movl %eax, -0x1cc(%rbp)
cmpl $0x0, -0x1cc(%rbp)
je 0x11ef1
movq -0x1a8(%rbp), %rax
movq %rax, -0x1d8(%rbp)
movq -0x1b8(%rbp), %rax
movq -0x1b0(%rbp), %rcx
movzbl (%rcx), %ecx
movzbl (%rax,%rcx), %eax
movl %eax, -0x1dc(%rbp)
movq -0x1b8(%rbp), %rax
movq -0x1b0(%rbp), %rcx
movzbl 0x1(%rcx), %ecx
movzbl (%rax,%rcx), %eax
shll $0x8, %eax
orl -0x1dc(%rbp), %eax
movl %eax, -0x1dc(%rbp)
movq -0x1b8(%rbp), %rax
movq -0x1b0(%rbp), %rcx
movzbl 0x2(%rcx), %ecx
movzbl (%rax,%rcx), %eax
shll $0x10, %eax
orl -0x1dc(%rbp), %eax
movl %eax, -0x1dc(%rbp)
movq -0x1b8(%rbp), %rax
movq -0x1b0(%rbp), %rcx
movzbl 0x3(%rcx), %ecx
movzbl (%rax,%rcx), %eax
shll $0x18, %eax
orl -0x1dc(%rbp), %eax
movl %eax, -0x1dc(%rbp)
movl -0x1dc(%rbp), %ecx
movq -0x1d8(%rbp), %rax
movl %ecx, (%rax)
movl -0x1cc(%rbp), %eax
movl %eax, -0x1e0(%rbp)
movl -0x130(%rbp), %eax
andl $0x3, %eax
movl %eax, -0x1e8(%rbp)
subl $0x1, %eax
je 0x11fa3
jmp 0x11f17
movl -0x1e8(%rbp), %eax
subl $0x2, %eax
je 0x11f6c
jmp 0x11f24
movl -0x1e8(%rbp), %eax
subl $0x3, %eax
jne 0x11fd0
jmp 0x11f35
movq -0x1b8(%rbp), %rax
movq -0x1b0(%rbp), %rcx
movl -0x1e0(%rbp), %edx
addl $0x2, %edx
movslq %edx, %rdx
movzbl (%rcx,%rdx), %ecx
movb (%rax,%rcx), %dl
movq -0x1a8(%rbp), %rax
movl -0x1e0(%rbp), %ecx
addl $0x2, %ecx
movslq %ecx, %rcx
movb %dl, (%rax,%rcx)
movq -0x1b8(%rbp), %rax
movq -0x1b0(%rbp), %rcx
movl -0x1e0(%rbp), %edx
addl $0x1, %edx
movslq %edx, %rdx
movzbl (%rcx,%rdx), %ecx
movb (%rax,%rcx), %dl
movq -0x1a8(%rbp), %rax
movl -0x1e0(%rbp), %ecx
addl $0x1, %ecx
movslq %ecx, %rcx
movb %dl, (%rax,%rcx)
movq -0x1b8(%rbp), %rax
movq -0x1b0(%rbp), %rcx
movslq -0x1e0(%rbp), %rdx
movzbl (%rcx,%rdx), %ecx
movb (%rax,%rcx), %dl
movq -0x1a8(%rbp), %rax
movslq -0x1e0(%rbp), %rcx
movb %dl, (%rax,%rcx)
jmp 0x11fd2
addq $0x1f0, %rsp            # imm = 0x1F0
popq %rbp
retq
nopl (%rax,%rax)