/*
 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
 *                         University Research and Technology
 *                         Corporation.  All rights reserved.
 * Copyright (c) 2004-2006 The University of Tennessee and The University
 *                         of Tennessee Research Foundation.  All rights
 *                         reserved.
 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
 *                         University of Stuttgart.  All rights reserved.
 * Copyright (c) 2004-2005 The Regents of the University of California.
 *                         All rights reserved.
 * Copyright (c) 2009      IBM Corporation.  All rights reserved.
 * Copyright (c) 2009      Los Alamos National Security, LLC.  All rights
 *                         reserved.
 * $COPYRIGHT$
 *
 * Additional copyrights may follow
 *
 * $HEADER$
 */

#ifndef _OPAL_CRC_H_
#define _OPAL_CRC_H_

#include "opal_config.h"

#include <stddef.h>

BEGIN_C_DECLS

#define CRC_POLYNOMIAL ((unsigned int)0x04c11db7)
#define CRC_INITIAL_REGISTER ((unsigned int)0xffffffff)


#define OPAL_CSUM( SRC, LEN )  opal_uicsum( SRC, LEN )
#define OPAL_CSUM_PARTIAL( SRC, LEN, UI1, UI2 ) \
    opal_uicsum_partial( SRC, LEN, UI1, UI2 )
#define OPAL_CSUM_BCOPY_PARTIAL( SRC, DST, LEN1, LEN2, UI1, UI2 ) \
    opal_bcopy_uicsum_partial( SRC, DST, LEN1, LEN2, UI1, UI2 )
#define OPAL_CSUM_ZERO  0


OPAL_DECLSPEC unsigned long
opal_bcopy_csum_partial(
    const void *  source,
    void *  destination,
    size_t copylen,
    size_t csumlen,
    unsigned long*  lastPartialLong,
    size_t*  lastPartialLength
    );

static inline unsigned long
opal_bcopy_csum (
    const void *  source,
    void *  destination,
    size_t copylen,
    size_t csumlen
    )
{
    unsigned long plong = 0;
    size_t plength = 0;
    return opal_bcopy_csum_partial(source, destination, copylen, csumlen, &plong, &plength);
}

OPAL_DECLSPEC unsigned int
opal_bcopy_uicsum_partial (
    const void *  source,
    void *  destination,
    size_t copylen,
    size_t csumlen,
    unsigned int*  lastPartialInt,
    size_t*  lastPartialLength
    );

static inline unsigned int
opal_bcopy_uicsum (
    const void *  source,
    void *  destination,
    size_t copylen,
    size_t csumlen
    )
{
    unsigned int pint = 0;
    size_t plength = 0;
    return opal_bcopy_uicsum_partial(source, destination, copylen, csumlen, &pint, &plength);
}

OPAL_DECLSPEC unsigned long
opal_csum_partial (
    const void *  source,
    size_t csumlen,
    unsigned long*  lastPartialLong,
    size_t*  lastPartialLength
    );


static inline unsigned long
opal_csum(const void *  source, size_t csumlen)
{
    unsigned long lastPartialLong = 0;
    size_t lastPartialLength = 0;
    return opal_csum_partial(source, csumlen, &lastPartialLong, &lastPartialLength);
}
/*
 * The buffer passed to this function is assumed to be 16-bit aligned
 */
static inline uint16_t
opal_csum16 (const void *  source, size_t csumlen)
{
    uint16_t *src = (uint16_t *) source;
    register uint32_t csum = 0;

    while (csumlen > 1) {
	    csum += *src++;
        csumlen -= 2;
    }
    /* Add leftover byte, if any */
    if(csumlen > 0)
        csum += *((unsigned char*)src);
    /* Fold 32-bit checksum to 16 bits */
    while(csum >> 16) {
        csum = (csum & 0xFFFF) + (csum >> 16);
    }
    return csum;
}

OPAL_DECLSPEC unsigned int
opal_uicsum_partial (
    const void *  source,
    size_t csumlen,
    unsigned int *  lastPartialInt,
    size_t*  lastPartialLength
    );

static inline unsigned int
opal_uicsum(const void *  source, size_t csumlen)
{
    unsigned int lastPartialInt = 0;
    size_t lastPartialLength = 0;
    return opal_uicsum_partial(source, csumlen, &lastPartialInt, &lastPartialLength);
}

/*
 * CRC Support
 */

void opal_initialize_crc_table(void);

OPAL_DECLSPEC unsigned int
opal_bcopy_uicrc_partial(
    const void *  source,
    void *  destination,
    size_t copylen,
    size_t crclen,
    unsigned int partial_crc);

static inline unsigned int
opal_bcopy_uicrc(
    const void *  source,
    void *  destination,
    size_t copylen,
    size_t crclen)
{
    return opal_bcopy_uicrc_partial(source, destination, copylen, crclen, CRC_INITIAL_REGISTER);
}

OPAL_DECLSPEC unsigned int
opal_uicrc_partial(
    const void *  source,
    size_t crclen,
    unsigned int partial_crc);


static inline unsigned int
opal_uicrc(const void *  source, size_t crclen)
{
    return opal_uicrc_partial(source, crclen, CRC_INITIAL_REGISTER);
}

END_C_DECLS

#endif