#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <time.h>
#include <endian.h>

// This is STILL faster!
__attribute__((noinline))
uint16_t cksum_bytes( void *data, unsigned int length )
{
  uint16_t *ptr;
  uint32_t sum;

  sum = 0;
  ptr = data;  
  while ( length > 1 )
  {
    sum += *ptr++;
    length -= 2;
  }

  // if there is any additional bytes remaining...
  if ( length )
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
   sum += ( uint16_t )( *( uint8_t * )ptr) << 8;   // last byte must be
                                                   // aligned to upper 8 bits.
#else
   sum += *( uint8_t * )ptr;
#endif

  // Add carry-outs...
  sum = ( sum & 0xffffU ) + ( sum >> 16 );

  // NOTE: Let the caller put this in network order, if necessary!
  return ~sum;
}

// 16-bit words are added two at a time (two per 32-bit int). This function
// sums the left and right 16-bit words and then the sum's resultant carry.
static uint16_t from32to16(uint32_t x)
{
  // add up 16-bit and 16-bit for 16+c bit.
  x = (x & 0xffff) + (x >> 16);

  // add up carry (again).
  x = (x & 0xffff) + (x >> 16);

  return x;
}

__attribute__((noinline))
uint16_t cksum_dwords( void *data, unsigned int size )
{
  unsigned char *buffer = data;
  unsigned int result = 0;

  if (size >= 4)
  {
    // Marks the offset into the buffer of the last 4 bytes.
    const unsigned char * const end = buffer + (size & ~3);
    unsigned int carry = 0;

    // Adds 32-bits at a time, storing overflows in "carry".
    do
    {
      unsigned int w = *(unsigned int *) buffer;

      buffer += 4;

      result += carry;
      result += w;

      carry = w > result;
    } while (buffer < end);

    // Adds the accumulated carrys back into the sum.
    result += carry;
    result = (result & 0xffff) + (result >> 16);
  }

  if (size & 2)
  {
    result += *(unsigned short *) buffer;
    buffer += 2;
  }

  if (size & 1)
    result += *buffer;

  result = from32to16(result);

  // Endianess!
  #if BYTE_ORDER == BIG_ENDIAN
    result = __builtin_bswap16( (uint16_t) result );
  #endif

  return ~result;
}

#include <cpuid.h>
#include <x86intrin.h>

static inline uint64_t start_( void )
{
  int a, b, c, d;

  __cpuid( 0, a, b, c, d );
  return _rdtsc();
}

static inline uint64_t end_( volatile uint64_t old )
{ return _rdtsc() - old; }

int main( void )
{
  #define MAX_BUFFER_SIZE 4096 /* 8 cache lines */

  static uint8_t buffer[MAX_BUFFER_SIZE] __attribute__((aligned(64)));
  uint8_t *endp = buffer + sizeof buffer;
  uint8_t *p = buffer;

  uint16_t cs1, cs2;
  uint64_t c1, c2;

  srand( time( NULL ) );
  while ( p < endp )
    *p++ = (uint32_t) rand() >> 23;

  c1 = start_();
  cs1 = cksum_bytes( buffer, sizeof buffer );
  c1 = end_( c1 );

  c2 = start_();
  cs2 = cksum_dwords( buffer, sizeof buffer );
  c2 = end_( c2 );

  printf( "cksum_bytes:  %#" PRIx16 " (%" PRIu64 " cycles)\n"
          "cksum_dwords: %#" PRIx16 " (%" PRIu64 " cycles)\n",
          cs1, c1, cs2, c2 );
}
