// File: /workspace/github_cpp_makefile_star2_O1/BU-DiSC[P]BF-Shared-Hashing/hash/Crc32.cpp
// Function: crc32_combine(unsigned int, unsigned int, unsigned long)
uint32_t crc32_combine(uint32_t crcA, uint32_t crcB, size_t lengthB)
{
  // based on Mark Adler's crc_combine from
  // https://github.com/madler/pigz/blob/master/pigz.c

  // main idea:
  // - if you have two equally-sized blocks A and B,
  //   then you can create a block C = A ^ B
  //   which has the property crc(C) = crc(A) ^ crc(B)
  // - if you append length(B) zeros to A and call it A' (think of it as AAAA000)
  //   and   prepend length(A) zeros to B and call it B' (think of it as 0000BBB)
  //   then exists a C' = A' ^ B'
  // - remember: if you XOR someting with zero, it remains unchanged: X ^ 0 = X
  // - that means C' = A concat B so that crc(A concat B) = crc(C') = crc(A') ^ crc(B')
  // - the trick is to compute crc(A') based on crc(A)
  //                       and crc(B') based on crc(B)
  // - since B' starts with many zeros, the crc of those initial zeros is still zero
  // - that means crc(B') = crc(B)
  // - unfortunately the trailing zeros of A' change the crc, so usually crc(A') != crc(A)
  // - the following code is a fast algorithm to compute crc(A')
  // - starting with crc(A) and appending length(B) zeros, needing just log2(length(B)) iterations
  // - the details are explained by the original author at
  //   https://stackoverflow.com/questions/23122312/crc-calculation-of-a-mostly-static-data-stream/23126768
  //
  // notes:
  // - I squeezed everything into one function to keep global namespace clean (original code two helper functions)
  // - most original comments are still in place, I added comments where these helper functions where made inline code
  // - performance-wise there isn't any differenze to the original zlib/pigz code

  // degenerated case
  if (lengthB == 0)
    return crcA;

  /// CRC32 => 32 bits
  const uint32_t CrcBits = 32;

  uint32_t odd [CrcBits]; // odd-power-of-two  zeros operator
  uint32_t even[CrcBits]; // even-power-of-two zeros operator

  // put operator for one zero bit in odd
  odd[0] = Polynomial;    // CRC-32 polynomial
  for (int i = 1; i < CrcBits; i++)
    odd[i] = 1 << (i - 1);

  // put operator for two zero bits in even
  // same as gf2_matrix_square(even, odd);
  for (int i = 0; i < CrcBits; i++)
  {
    uint32_t vec = odd[i];
    even[i] = 0;
    for (int j = 0; vec != 0; j++, vec >>= 1)
      if (vec & 1)
        even[i] ^= odd[j];
  }
  // put operator for four zero bits in odd
  // same as gf2_matrix_square(odd, even);
  for (int i = 0; i < CrcBits; i++)
  {
    uint32_t vec = even[i];
    odd[i] = 0;
    for (int j = 0; vec != 0; j++, vec >>= 1)
      if (vec & 1)
        odd[i] ^= even[j];
  }

  // the following loop becomes much shorter if I keep swapping even and odd
  uint32_t* a = even;
  uint32_t* b = odd;
  // apply secondLength zeros to firstCrc32
  for (; lengthB > 0; lengthB >>= 1)
  {
    // same as gf2_matrix_square(a, b);
    for (int i = 0; i < CrcBits; i++)
    {
      uint32_t vec = b[i];
      a[i] = 0;
      for (int j = 0; vec != 0; j++, vec >>= 1)
        if (vec & 1)
          a[i] ^= b[j];
    }

    // apply zeros operator for this bit
    if (lengthB & 1)
    {
      // same as firstCrc32 = gf2_matrix_times(a, firstCrc32);
      uint32_t sum = 0;
      for (int i = 0; crcA != 0; i++, crcA >>= 1)
        if (crcA & 1)
          sum ^= a[i];
      crcA = sum;
    }

    // switch even and odd
    uint32_t* t = a; a = b; b = t;
  }

  // return combined crc
  return crcA ^ crcB;
}

// Assembly:
movl %edi, %eax
testq %rdx, %rdx
je 0x27e23
subq $0x88, %rsp
movl $0xedb88320, -0x80(%rsp) # imm = 0xEDB88320
xorl %ecx, %ecx
movdqa 0x1505(%rip), %xmm0     # 0x29190
movdqa 0x150d(%rip), %xmm1     # 0x291a0
movdqa 0x2a65(%rip), %xmm2     # 0x2a700
pshufd $0xf5, %xmm2, %xmm3     # xmm3 = xmm2[1,1,3,3]
movq %rcx, %xmm4
pshufd $0x44, %xmm4, %xmm4     # xmm4 = xmm4[0,1,0,1]
por %xmm0, %xmm4
pxor %xmm1, %xmm4
movdqa %xmm2, %xmm5
pcmpgtd %xmm4, %xmm5
pshufd $0xf5, %xmm4, %xmm4     # xmm4 = xmm4[1,1,3,3]
pcmpeqd %xmm3, %xmm4
pand %xmm5, %xmm4
movd %xmm4, %edi
testb $0x1, %dil
je 0x27cdc
movl $0x1, %edi
shll %cl, %edi
movl %edi, -0x7c(%rsp,%rcx,4)
pextrw $0x4, %xmm4, %edi
testb $0x1, %dil
je 0x27cf2
movl $0x2, %edi
shll %cl, %edi
movl %edi, -0x78(%rsp,%rcx,4)
addq $0x2, %rcx
cmpq $0x20, %rcx
jne 0x27ca0
xorl %ecx, %ecx
leaq -0x80(%rsp), %rdi
movl -0x80(%rsp,%rcx,4), %r8d
movl $0x0, (%rsp,%rcx,4)
testl %r8d, %r8d
je 0x27d37
movq %rdi, %r9
testb $0x1, %r8b
je 0x27d24
movl (%r9), %r10d
xorl %r10d, (%rsp,%rcx,4)
movl %r8d, %r10d
shrl %r10d
addq $0x4, %r9
cmpl $0x2, %r8d
movl %r10d, %r8d
jae 0x27d17
incq %rcx
cmpq $0x20, %rcx
jne 0x27d03
xorl %ecx, %ecx
movq %rsp, %rdi
movl (%rsp,%rcx,4), %r8d
movl $0x0, -0x80(%rsp,%rcx,4)
testl %r8d, %r8d
je 0x27d7a
movq %rdi, %r9
testb $0x1, %r8b
je 0x27d67
movl (%r9), %r10d
xorl %r10d, -0x80(%rsp,%rcx,4)
movl %r8d, %r10d
shrl %r10d
addq $0x4, %r9
cmpl $0x2, %r8d
movl %r10d, %r8d
jae 0x27d59
incq %rcx
cmpq $0x20, %rcx
jne 0x27d45
movq %rsp, %rcx
leaq -0x80(%rsp), %r9
movl %eax, %r8d
movq %rcx, %rdi
movq %r9, %rcx
xorl %eax, %eax
movl (%rcx,%rax,4), %r9d
movl $0x0, (%rdi,%rax,4)
movq %rcx, %r10
testl %r9d, %r9d
je 0x27dc9
testb $0x1, %r9b
je 0x27db6
movl (%r10), %r11d
xorl %r11d, (%rdi,%rax,4)
movl %r9d, %r11d
shrl %r11d
addq $0x4, %r10
cmpl $0x2, %r9d
movl %r11d, %r9d
jae 0x27da9
incq %rax
cmpq $0x20, %rax
jne 0x27d96
testb $0x1, %dl
jne 0x27ddc
movl %r8d, %eax
jmp 0x27e04
xorl %eax, %eax
testl %r8d, %r8d
je 0x27e04
xorl %eax, %eax
movq %rdi, %r9
testb $0x1, %r8b
je 0x27df1
xorl (%r9), %eax
movl %r8d, %r10d
shrl %r10d
addq $0x4, %r9
cmpl $0x1, %r8d
movl %r10d, %r8d
ja 0x27de8
movq %rdx, %r8
shrq %r8
movq %rdi, %r9
cmpq $0x1, %rdx
movq %r8, %rdx
ja 0x27d8b
xorl %esi, %eax
addq $0x88, %rsp
retq