/*
 * 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) 2014      Intel, Inc. All rights reserved.
 * $COPYRIGHT$
 *
 * Additional copyrights may follow
 *
 * $HEADER$
 */

#include "opal_config.h"
#ifdef HAVE_STDIO_H
#    include <stdio.h>
#endif /* HAVE_STDIO_H */
#include <stdlib.h>
#ifdef HAVE_STRINGS_H
#    include <strings.h>
#endif /* HAVE_STRINGS_H */
#include <string.h>
#ifdef HAVE_UNISTD_H
#    include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include "opal/util/crc.h"

#if (OPAL_ALIGNMENT_LONG == 8)
#    define OPAL_CRC_WORD_MASK_ 0x7
#elif (OPAL_ALIGNMENT_LONG == 4)
#    define OPAL_CRC_WORD_MASK_ 0x3
#else
#    define OPAL_CRC_WORD_MASK_ 0xFFFF
#endif

#define WORDALIGNED(v) (((intptr_t) v & OPAL_CRC_WORD_MASK_) ? false : true)

#define INTALIGNED(v) (((intptr_t) v & 3) ? false : true)

/*
 * this version of bcopy_csum() looks a little too long, but it
 * handles cumulative checksumming for arbitrary lengths and address
 * alignments as best as it can; the contents of lastPartialLong and
 * lastPartialLength are updated to reflected the last partial word's
 * value and length (in bytes) -- this should allow proper handling of
 * checksumming contiguous or noncontiguous buffers via multiple calls
 * of bcopy_csum() - Mitch
 */

unsigned long opal_bcopy_csum_partial(const void *source, void *destination, size_t copylen,
                                      size_t csumlen, unsigned long *lastPartialLong,
                                      size_t *lastPartialLength)
{
    unsigned long *src = (unsigned long *) source;
    unsigned long *dest = (unsigned long *) destination;
    unsigned long csum = 0;
    size_t csumlenresidue;
    unsigned long i, temp;

    csumlenresidue = (csumlen > copylen) ? (csumlen - copylen) : 0;
    temp = *lastPartialLong;

    if (WORDALIGNED(source) && WORDALIGNED(dest)) {
        if (*lastPartialLength) {
            /* do we have enough data to fill out the partial word? */
            if (copylen >= (sizeof(unsigned long) - *lastPartialLength)) { /* YES, we do... */
                memcpy(((char *) &temp + *lastPartialLength), src,
                       (sizeof(unsigned long) - *lastPartialLength));
                memcpy(dest, ((char *) &temp + *lastPartialLength),
                       (sizeof(unsigned long) - *lastPartialLength));
                src = (unsigned long *) ((char *) src + sizeof(unsigned long) - *lastPartialLength);
                dest = (unsigned long *) ((char *) dest + sizeof(unsigned long)
                                          - *lastPartialLength);
                csum += (temp - *lastPartialLong);
                copylen -= sizeof(unsigned long) - *lastPartialLength;
                /* now we have an unaligned source and an unaligned destination */
                for (; copylen >= sizeof(*src); copylen -= sizeof(*src)) {
                    memcpy(&temp, src, sizeof(temp));
                    src++;
                    csum += temp;
                    memcpy(dest, &temp, sizeof(temp));
                    dest++;
                }
                *lastPartialLength = 0;
                *lastPartialLong = 0;
            } else { /* NO, we don't... */
                memcpy(((char *) &temp + *lastPartialLength), src, copylen);
                memcpy(dest, ((char *) &temp + *lastPartialLength), copylen);
                src = (unsigned long *) ((char *) src + copylen);
                dest = (unsigned long *) ((char *) dest + copylen);
                csum += (temp - *lastPartialLong);
                *lastPartialLong = temp;
                *lastPartialLength += copylen;
                copylen = 0;
            }
        } else { /* fast path... */
            size_t numLongs = copylen / sizeof(unsigned long);
            for (i = 0; i < numLongs; i++) {
                csum += *src;
                *dest++ = *src++;
            }
            *lastPartialLong = 0;
            *lastPartialLength = 0;
            if (WORDALIGNED(copylen) && (csumlenresidue == 0)) {
                return (csum);
            } else {
                copylen -= i * sizeof(unsigned long);
            }
        }
    } else if (WORDALIGNED(source)) {
        if (*lastPartialLength) {
            /* do we have enough data to fill out the partial word? */
            if (copylen >= (sizeof(unsigned long) - *lastPartialLength)) { /* YES, we do... */
                memcpy(((char *) &temp + *lastPartialLength), src,
                       (sizeof(unsigned long) - *lastPartialLength));
                memcpy(dest, ((char *) &temp + *lastPartialLength),
                       (sizeof(unsigned long) - *lastPartialLength));
                src = (unsigned long *) ((char *) src + sizeof(unsigned long) - *lastPartialLength);
                dest = (unsigned long *) ((char *) dest + sizeof(unsigned long)
                                          - *lastPartialLength);
                csum += (temp - *lastPartialLong);
                copylen -= sizeof(unsigned long) - *lastPartialLength;
                /* now we have an unaligned source and an unknown alignment for our destination */
                if (WORDALIGNED(dest)) {
                    size_t numLongs = copylen / sizeof(unsigned long);
                    for (i = 0; i < numLongs; i++) {
                        memcpy(&temp, src, sizeof(temp));
                        src++;
                        csum += temp;
                        *dest++ = temp;
                    }
                    copylen -= i * sizeof(unsigned long);
                } else {
                    for (; copylen >= sizeof(*src); copylen -= sizeof(*src)) {
                        memcpy(&temp, src, sizeof(temp));
                        src++;
                        csum += temp;
                        memcpy(dest, &temp, sizeof(temp));
                        dest++;
                    }
                }
                *lastPartialLong = 0;
                *lastPartialLength = 0;
            } else { /* NO, we don't... */
                memcpy(((char *) &temp + *lastPartialLength), src, copylen);
                memcpy(dest, ((char *) &temp + *lastPartialLength), copylen);
                src = (unsigned long *) ((char *) src + copylen);
                dest = (unsigned long *) ((char *) dest + copylen);
                csum += (temp - *lastPartialLong);
                *lastPartialLong = temp;
                *lastPartialLength += copylen;
                copylen = 0;
            }
        } else {
            for (; copylen >= sizeof(*src); copylen -= sizeof(*src)) {
                temp = *src++;
                csum += temp;
                memcpy(dest, &temp, sizeof(temp));
                dest++;
            }
            *lastPartialLong = 0;
            *lastPartialLength = 0;
        }
    } else if (WORDALIGNED(dest)) {
        if (*lastPartialLength) {
            /* do we have enough data to fill out the partial word? */
            if (copylen >= (sizeof(unsigned long) - *lastPartialLength)) { /* YES, we do... */
                memcpy(((char *) &temp + *lastPartialLength), src,
                       (sizeof(unsigned long) - *lastPartialLength));
                memcpy(dest, ((char *) &temp + *lastPartialLength),
                       (sizeof(unsigned long) - *lastPartialLength));
                src = (unsigned long *) ((char *) src + sizeof(unsigned long) - *lastPartialLength);
                dest = (unsigned long *) ((char *) dest + sizeof(unsigned long)
                                          - *lastPartialLength);
                csum += (temp - *lastPartialLong);
                copylen -= sizeof(unsigned long) - *lastPartialLength;
                /* now we have a source of unknown alignment and a unaligned destination */
                if (WORDALIGNED(src)) {
                    for (; copylen >= sizeof(*src); copylen -= sizeof(*src)) {
                        temp = *src++;
                        csum += temp;
                        memcpy(dest, &temp, sizeof(temp));
                        dest++;
                    }
                    *lastPartialLong = 0;
                    *lastPartialLength = 0;
                } else {
                    for (; copylen >= sizeof(*src); copylen -= sizeof(*src)) {
                        memcpy(&temp, src, sizeof(temp));
                        src++;
                        csum += temp;
                        memcpy(dest, &temp, sizeof(temp));
                        dest++;
                    }
                    *lastPartialLength = 0;
                    *lastPartialLong = 0;
                }
            } else { /* NO, we don't... */
                memcpy(((char *) &temp + *lastPartialLength), src, copylen);
                memcpy(dest, ((char *) &temp + *lastPartialLength), copylen);
                src = (unsigned long *) ((char *) src + copylen);
                dest = (unsigned long *) ((char *) dest + copylen);
                csum += (temp - *lastPartialLong);
                *lastPartialLong = temp;
                *lastPartialLength += copylen;
                copylen = 0;
            }
        } else {
            for (; copylen >= sizeof(*src); copylen -= sizeof(*src)) {
                memcpy(&temp, src, sizeof(temp));
                src++;
                csum += temp;
                *dest++ = temp;
            }
            *lastPartialLength = 0;
            *lastPartialLong = 0;
        }
    } else {
        if (*lastPartialLength) {
            /* do we have enough data to fill out the partial word? */
            if (copylen >= (sizeof(unsigned long) - *lastPartialLength)) { /* YES, we do... */
                memcpy(((char *) &temp + *lastPartialLength), src,
                       (sizeof(unsigned long) - *lastPartialLength));
                memcpy(dest, ((char *) &temp + *lastPartialLength),
                       (sizeof(unsigned long) - *lastPartialLength));
                src = (unsigned long *) ((char *) src + sizeof(unsigned long) - *lastPartialLength);
                dest = (unsigned long *) ((char *) dest + sizeof(unsigned long)
                                          - *lastPartialLength);
                csum += (temp - *lastPartialLong);
                copylen -= sizeof(unsigned long) - *lastPartialLength;
                /* now we have an unknown alignment for our source and destination */
                if (WORDALIGNED(src) && WORDALIGNED(dest)) {
                    size_t numLongs = copylen / sizeof(unsigned long);
                    for (i = 0; i < numLongs; i++) {
                        csum += *src;
                        *dest++ = *src++;
                    }
                    copylen -= i * sizeof(unsigned long);
                } else { /* safe but slower for all other alignments */
                    for (; copylen >= sizeof(*src); copylen -= sizeof(*src)) {
                        memcpy(&temp, src, sizeof(temp));
                        src++;
                        csum += temp;
                        memcpy(dest, &temp, sizeof(temp));
                        dest++;
                    }
                }
                *lastPartialLong = 0;
                *lastPartialLength = 0;
            } else { /* NO, we don't... */
                memcpy(((char *) &temp + *lastPartialLength), src, copylen);
                memcpy(dest, ((char *) &temp + *lastPartialLength), copylen);
                src = (unsigned long *) ((char *) src + copylen);
                dest = (unsigned long *) ((char *) dest + copylen);
                csum += (temp - *lastPartialLong);
                *lastPartialLong = temp;
                *lastPartialLength += copylen;
                copylen = 0;
            }
        } else {
            for (; copylen >= sizeof(*src); copylen -= sizeof(*src)) {
                memcpy(&temp, src, sizeof(temp));
                src++;
                csum += temp;
                memcpy(dest, &temp, sizeof(temp));
                dest++;
            }
            *lastPartialLength = 0;
            *lastPartialLong = 0;
        }
    }

    /* if copylen is non-zero there was a bit left, less than an unsigned long's worth */
    if ((copylen != 0) && (csumlenresidue == 0)) {
        temp = *lastPartialLong;
        if (*lastPartialLength) {
            if (copylen >= (sizeof(unsigned long) - *lastPartialLength)) {
                /* copy all remaining bytes from src to dest */
                unsigned long copytemp = 0;
                memcpy(&copytemp, src, copylen);
                memcpy(dest, &copytemp, copylen);
                /* fill out rest of partial word and add to checksum */
                memcpy(((char *) &temp + *lastPartialLength), src,
                       (sizeof(unsigned long) - *lastPartialLength));
                /* avoid unsigned arithmetic overflow by subtracting the old partial
                 * word from the new one before adding to the checksum...
                 */
                csum += (temp - *lastPartialLong);
                copylen -= sizeof(unsigned long) - *lastPartialLength;
                src = (unsigned long *) ((char *) src + sizeof(unsigned long) - *lastPartialLength);
                *lastPartialLength = copylen;
                /* reset temp, and calculate next partial word */
                temp = 0;
                if (copylen) {
                    memcpy(&temp, src, copylen);
                }
                /* add it to the the checksum */
                csum += temp;
                *lastPartialLong = temp;
            } else {
                /* copy all remaining bytes from src to dest */
                unsigned long copytemp = 0;
                memcpy(&copytemp, src, copylen);
                memcpy(dest, &copytemp, copylen);
                /* fill out rest of partial word and add to checksum */
                memcpy(((char *) &temp + *lastPartialLength), src, copylen);
                /* avoid unsigned arithmetic overflow by subtracting the old partial
                 * word from the new one before adding to the checksum...
                 */
                csum += temp - *lastPartialLong;
                *lastPartialLong = temp;
                *lastPartialLength += copylen;
            }
        } else { /* fast path... */
            /* temp and *lastPartialLong are 0 if *lastPartialLength is 0... */
            memcpy(&temp, src, copylen);
            csum += temp;
            memcpy(dest, &temp, copylen);
            *lastPartialLong = temp;
            *lastPartialLength = copylen;
            /* done...return the checksum */
        }
    } else if (csumlenresidue != 0) {
        if (copylen != 0) {
            temp = 0;
            memcpy(&temp, src, copylen);
            memcpy(dest, &temp, copylen);
        }
        if (csumlenresidue < (sizeof(unsigned long) - copylen - *lastPartialLength)) {
            temp = *lastPartialLong;
            memcpy(((char *) &temp + *lastPartialLength), src, (copylen + csumlenresidue));
            /* avoid unsigned arithmetic overflow by subtracting the old partial */
            /* word from the new one before adding to the checksum... */
            csum += temp - *lastPartialLong;
            src++;
            *lastPartialLong = temp;
            *lastPartialLength += copylen + csumlenresidue;
            csumlenresidue = 0;
        } else {
            /* we have enough chksum data to fill out our last partial */
            /* word */
            temp = *lastPartialLong;
            memcpy(((char *) &temp + *lastPartialLength), src,
                   (sizeof(unsigned long) - *lastPartialLength));
            /* avoid unsigned arithmetic overflow by subtracting the old partial */
            /* word from the new one before adding to the checksum... */
            csum += temp - *lastPartialLong;
            src = (unsigned long *) ((char *) src + sizeof(unsigned long) - *lastPartialLength);
            csumlenresidue -= sizeof(unsigned long) - *lastPartialLength - copylen;
            *lastPartialLength = 0;
            *lastPartialLong = 0;
        }
        if (WORDALIGNED(src)) {
            for (i = 0; i < csumlenresidue / sizeof(unsigned long); i++) {
                csum += *src++;
            }
        } else {
            for (i = 0; i < csumlenresidue / sizeof(unsigned long); i++) {
                memcpy(&temp, src, sizeof(temp));
                csum += temp;
                src++;
            }
        }
        csumlenresidue -= i * sizeof(unsigned long);
        if (csumlenresidue) {
            temp = 0;
            memcpy(&temp, src, csumlenresidue);
            csum += temp;
            *lastPartialLong = temp;
            *lastPartialLength = csumlenresidue;
        }
    } /* end else if (csumlenresidue != 0) */

    return csum;
}

unsigned int opal_bcopy_uicsum_partial(const void *source, void *destination, size_t copylen,
                                       size_t csumlen, unsigned int *lastPartialInt,
                                       size_t *lastPartialLength)
{
    unsigned int *src = (unsigned int *) source;
    unsigned int *dest = (unsigned int *) destination;
    unsigned int csum = 0;
    size_t csumlenresidue;
    unsigned long i;
    unsigned int temp;

    csumlenresidue = (csumlen > copylen) ? (csumlen - copylen) : 0;
    temp = *lastPartialInt;

    if (INTALIGNED(source) && INTALIGNED(dest)) {
        if (*lastPartialLength) {
            /* do we have enough data to fill out the partial word? */
            if (copylen >= (sizeof(unsigned int) - *lastPartialLength)) { /* YES, we do... */
                memcpy(((char *) &temp + *lastPartialLength), src,
                       (sizeof(unsigned int) - *lastPartialLength));
                memcpy(dest, ((char *) &temp + *lastPartialLength),
                       (sizeof(unsigned int) - *lastPartialLength));
                src = (unsigned int *) ((char *) src + sizeof(unsigned int) - *lastPartialLength);
                dest = (unsigned int *) ((char *) dest + sizeof(unsigned int) - *lastPartialLength);
                csum += (temp - *lastPartialInt);
                copylen -= sizeof(unsigned int) - *lastPartialLength;
                /* now we have an unaligned source and an unaligned destination */
                for (; copylen >= sizeof(*src); copylen -= sizeof(*src)) {
                    memcpy(&temp, src, sizeof(temp));
                    src++;
                    csum += temp;
                    memcpy(dest, &temp, sizeof(temp));
                    dest++;
                }
                *lastPartialLength = 0;
                *lastPartialInt = 0;
            } else { /* NO, we don't... */
                memcpy(((char *) &temp + *lastPartialLength), src, copylen);
                memcpy(dest, ((char *) &temp + *lastPartialLength), copylen);
                src = (unsigned int *) ((char *) src + copylen);
                dest = (unsigned int *) ((char *) dest + copylen);
                csum += (temp - *lastPartialInt);
                *lastPartialInt = temp;
                *lastPartialLength += copylen;
                copylen = 0;
            }
        } else { /* fast path... */
            size_t numLongs = copylen / sizeof(unsigned int);
            for (i = 0; i < numLongs; i++) {
                csum += *src;
                *dest++ = *src++;
            }
            *lastPartialInt = 0;
            *lastPartialLength = 0;
            if (INTALIGNED(copylen) && (csumlenresidue == 0)) {
                return (csum);
            } else {
                copylen -= i * sizeof(unsigned int);
            }
        }
    } else if (INTALIGNED(source)) {
        if (*lastPartialLength) {
            /* do we have enough data to fill out the partial word? */
            if (copylen >= (sizeof(unsigned int) - *lastPartialLength)) { /* YES, we do... */
                memcpy(((char *) &temp + *lastPartialLength), src,
                       (sizeof(unsigned int) - *lastPartialLength));
                memcpy(dest, ((char *) &temp + *lastPartialLength),
                       (sizeof(unsigned int) - *lastPartialLength));
                src = (unsigned int *) ((char *) src + sizeof(unsigned int) - *lastPartialLength);
                dest = (unsigned int *) ((char *) dest + sizeof(unsigned int) - *lastPartialLength);
                csum += (temp - *lastPartialInt);
                copylen -= sizeof(unsigned int) - *lastPartialLength;
                /* now we have an unaligned source and an unknown alignment for our destination */
                if (INTALIGNED(dest)) {
                    size_t numLongs = copylen / sizeof(unsigned int);
                    for (i = 0; i < numLongs; i++) {
                        memcpy(&temp, src, sizeof(temp));
                        src++;
                        csum += temp;
                        *dest++ = temp;
                    }
                    copylen -= i * sizeof(unsigned int);
                } else {
                    for (; copylen >= sizeof(*src); copylen -= sizeof(*src)) {
                        memcpy(&temp, src, sizeof(temp));
                        src++;
                        csum += temp;
                        memcpy(dest, &temp, sizeof(temp));
                        dest++;
                    }
                }
                *lastPartialInt = 0;
                *lastPartialLength = 0;
            } else { /* NO, we don't... */
                memcpy(((char *) &temp + *lastPartialLength), src, copylen);
                memcpy(dest, ((char *) &temp + *lastPartialLength), copylen);
                src = (unsigned int *) ((char *) src + copylen);
                dest = (unsigned int *) ((char *) dest + copylen);
                csum += (temp - *lastPartialInt);
                *lastPartialInt = temp;
                *lastPartialLength += copylen;
                copylen = 0;
            }
        } else {
            for (; copylen >= sizeof(*src); copylen -= sizeof(*src)) {
                temp = *src++;
                csum += temp;
                memcpy(dest, &temp, sizeof(temp));
                dest++;
            }
            *lastPartialInt = 0;
            *lastPartialLength = 0;
        }
    } else if (INTALIGNED(dest)) {
        if (*lastPartialLength) {
            /* do we have enough data to fill out the partial word? */
            if (copylen >= (sizeof(unsigned int) - *lastPartialLength)) { /* YES, we do... */
                memcpy(((char *) &temp + *lastPartialLength), src,
                       (sizeof(unsigned int) - *lastPartialLength));
                memcpy(dest, ((char *) &temp + *lastPartialLength),
                       (sizeof(unsigned int) - *lastPartialLength));
                src = (unsigned int *) ((char *) src + sizeof(unsigned int) - *lastPartialLength);
                dest = (unsigned int *) ((char *) dest + sizeof(unsigned int) - *lastPartialLength);
                csum += (temp - *lastPartialInt);
                copylen -= sizeof(unsigned int) - *lastPartialLength;
                /* now we have a source of unknown alignment and a unaligned destination */
                if (INTALIGNED(src)) {
                    for (; copylen >= sizeof(*src); copylen -= sizeof(*src)) {
                        temp = *src++;
                        csum += temp;
                        memcpy(dest, &temp, sizeof(temp));
                        dest++;
                    }
                    *lastPartialInt = 0;
                    *lastPartialLength = 0;
                } else {
                    for (; copylen >= sizeof(*src); copylen -= sizeof(*src)) {
                        memcpy(&temp, src, sizeof(temp));
                        src++;
                        csum += temp;
                        memcpy(dest, &temp, sizeof(temp));
                        dest++;
                    }
                    *lastPartialLength = 0;
                    *lastPartialInt = 0;
                }
            } else { /* NO, we don't... */
                memcpy(((char *) &temp + *lastPartialLength), src, copylen);
                memcpy(dest, ((char *) &temp + *lastPartialLength), copylen);
                src = (unsigned int *) ((char *) src + copylen);
                dest = (unsigned int *) ((char *) dest + copylen);
                csum += (temp - *lastPartialInt);
                *lastPartialInt = temp;
                *lastPartialLength += copylen;
                copylen = 0;
            }
        } else {
            for (; copylen >= sizeof(*src); copylen -= sizeof(*src)) {
                memcpy(&temp, src, sizeof(temp));
                src++;
                csum += temp;
                *dest++ = temp;
            }
            *lastPartialLength = 0;
            *lastPartialInt = 0;
        }
    } else {
        if (*lastPartialLength) {
            /* do we have enough data to fill out the partial word? */
            if (copylen >= (sizeof(unsigned int) - *lastPartialLength)) { /* YES, we do... */
                memcpy(((char *) &temp + *lastPartialLength), src,
                       (sizeof(unsigned int) - *lastPartialLength));
                memcpy(dest, ((char *) &temp + *lastPartialLength),
                       (sizeof(unsigned int) - *lastPartialLength));
                src = (unsigned int *) ((char *) src + sizeof(unsigned int) - *lastPartialLength);
                dest = (unsigned int *) ((char *) dest + sizeof(unsigned int) - *lastPartialLength);
                csum += (temp - *lastPartialInt);
                copylen -= sizeof(unsigned int) - *lastPartialLength;
                /* now we have an unknown alignment for our source and destination */
                if (INTALIGNED(src) && INTALIGNED(dest)) {
                    size_t numLongs = copylen / sizeof(unsigned int);
                    for (i = 0; i < numLongs; i++) {
                        csum += *src;
                        *dest++ = *src++;
                    }
                    copylen -= i * sizeof(unsigned int);
                } else { /* safe but slower for all other alignments */
                    for (; copylen >= sizeof(*src); copylen -= sizeof(*src)) {
                        memcpy(&temp, src, sizeof(temp));
                        src++;
                        csum += temp;
                        memcpy(dest, &temp, sizeof(temp));
                        dest++;
                    }
                }
                *lastPartialInt = 0;
                *lastPartialLength = 0;
            } else { /* NO, we don't... */
                memcpy(((char *) &temp + *lastPartialLength), src, copylen);
                memcpy(dest, ((char *) &temp + *lastPartialLength), copylen);
                src = (unsigned int *) ((char *) src + copylen);
                dest = (unsigned int *) ((char *) dest + copylen);
                csum += (temp - *lastPartialInt);
                *lastPartialInt = temp;
                *lastPartialLength += copylen;
                copylen = 0;
            }
        } else {
            for (; copylen >= sizeof(*src); copylen -= sizeof(*src)) {
                memcpy(&temp, src, sizeof(temp));
                src++;
                csum += temp;
                memcpy(dest, &temp, sizeof(temp));
                dest++;
            }
            *lastPartialLength = 0;
            *lastPartialInt = 0;
        }
    }

    /* if copylen is non-zero there was a bit left, less than an unsigned int's worth */
    if ((copylen != 0) && (csumlenresidue == 0)) {
        temp = *lastPartialInt;
        if (*lastPartialLength) {
            if (copylen >= (sizeof(unsigned int) - *lastPartialLength)) {
                /* copy all remaining bytes from src to dest */
                unsigned int copytemp = 0;
                memcpy(&copytemp, src, copylen);
                memcpy(dest, &copytemp, copylen);
                /* fill out rest of partial word and add to checksum */
                memcpy(((char *) &temp + *lastPartialLength), src,
                       (sizeof(unsigned int) - *lastPartialLength));
                /* avoid unsigned arithmetic overflow by subtracting the old partial
                 * word from the new one before adding to the checksum...
                 */
                csum += (temp - *lastPartialInt);
                copylen -= sizeof(unsigned int) - *lastPartialLength;
                src = (unsigned int *) ((char *) src + sizeof(unsigned int) - *lastPartialLength);
                *lastPartialLength = copylen;
                /* reset temp, and calculate next partial word */
                temp = 0;
                if (copylen) {
                    memcpy(&temp, src, copylen);
                }
                /* add it to the the checksum */
                csum += temp;
                *lastPartialInt = temp;
            } else {
                /* copy all remaining bytes from src to dest */
                unsigned int copytemp = 0;
                memcpy(&copytemp, src, copylen);
                memcpy(dest, &copytemp, copylen);
                /* fill out rest of partial word and add to checksum */
                memcpy(((char *) &temp + *lastPartialLength), src, copylen);
                /* avoid unsigned arithmetic overflow by subtracting the old partial
                 * word from the new one before adding to the checksum...
                 */
                csum += temp - *lastPartialInt;
                *lastPartialInt = temp;
                *lastPartialLength += copylen;
            }
        } else { /* fast path... */
            /* temp and *lastPartialInt are 0 if *lastPartialLength is 0... */
            memcpy(&temp, src, copylen);
            csum += temp;
            memcpy(dest, &temp, copylen);
            *lastPartialInt = temp;
            *lastPartialLength = copylen;
            /* done...return the checksum */
        }
    } else if (csumlenresidue != 0) {
        if (copylen != 0) {
            temp = 0;
            memcpy(&temp, src, copylen);
            memcpy(dest, &temp, copylen);
        }
        if (csumlenresidue < (sizeof(unsigned int) - copylen - *lastPartialLength)) {
            temp = *lastPartialInt;
            memcpy(((char *) &temp + *lastPartialLength), src, (copylen + csumlenresidue));
            /* avoid unsigned arithmetic overflow by subtracting the old partial
             * word from the new one before adding to the checksum...
             */
            csum += temp - *lastPartialInt;
            src++;
            *lastPartialInt = temp;
            *lastPartialLength += copylen + csumlenresidue;
            csumlenresidue = 0;
        } else {
            /* we have enough chksum data to fill out our last partial
             * word
             */
            temp = *lastPartialInt;
            memcpy(((char *) &temp + *lastPartialLength), src,
                   (sizeof(unsigned int) - *lastPartialLength));
            /* avoid unsigned arithmetic overflow by subtracting the old partial
             * word from the new one before adding to the checksum...
             */
            csum += temp - *lastPartialInt;
            src = (unsigned int *) ((char *) src + sizeof(unsigned int) - *lastPartialLength);
            csumlenresidue -= sizeof(unsigned int) - *lastPartialLength - copylen;
            *lastPartialLength = 0;
            *lastPartialInt = 0;
        }
        if (INTALIGNED(src)) {
            for (i = 0; i < csumlenresidue / sizeof(unsigned int); i++) {
                csum += *src++;
            }
        } else {
            for (i = 0; i < csumlenresidue / sizeof(unsigned int); i++) {
                memcpy(&temp, src, sizeof(temp));
                csum += temp;
                src++;
            }
        }
        csumlenresidue -= i * sizeof(unsigned int);
        if (csumlenresidue) {
            temp = 0;
            memcpy(&temp, src, csumlenresidue);
            csum += temp;
            *lastPartialInt = temp;
            *lastPartialLength = csumlenresidue;
        }
    } /* end else if (csumlenresidue != 0) */

    return csum;
}

/*
 * csum() generates a bcopy_csum() - compatible checksum that can be
 * called multiple times
 */

unsigned long opal_csum_partial(const void *source, size_t csumlen, unsigned long *lastPartialLong,
                                size_t *lastPartialLength)
{
    unsigned long *src = (unsigned long *) source;
    unsigned long csum = 0;
    unsigned long i, temp;

    temp = *lastPartialLong;

    if (WORDALIGNED(source)) {
        if (*lastPartialLength) {
            /* do we have enough data to fill out the partial word? */
            if (csumlen >= (sizeof(unsigned long) - *lastPartialLength)) { /* YES, we do... */
                memcpy(((char *) &temp + *lastPartialLength), src,
                       (sizeof(unsigned long) - *lastPartialLength));
                src = (unsigned long *) ((char *) src + sizeof(unsigned long) - *lastPartialLength);
                csum += (temp - *lastPartialLong);
                csumlen -= sizeof(unsigned long) - *lastPartialLength;
                /* now we have an unaligned source */
                for (i = 0; i < csumlen / sizeof(unsigned long); i++) {
                    memcpy(&temp, src, sizeof(temp));
                    csum += temp;
                    src++;
                }
                csumlen -= i * sizeof(unsigned long);
                *lastPartialLong = 0;
                *lastPartialLength = 0;
            } else { /* NO, we don't... */
                memcpy(((char *) &temp + *lastPartialLength), src, csumlen);
                src = (unsigned long *) ((char *) src + csumlen);
                csum += (temp - *lastPartialLong);
                *lastPartialLong = temp;
                *lastPartialLength += csumlen;
                csumlen = 0;
            }
        } else { /* fast path... */
            size_t numLongs = csumlen / sizeof(unsigned long);
            for (i = 0; i < numLongs; i++) {
                csum += *src++;
            }
            *lastPartialLong = 0;
            *lastPartialLength = 0;
            if (WORDALIGNED(csumlen)) {
                return (csum);
            } else {
                csumlen -= i * sizeof(unsigned long);
            }
        }
    } else {
        if (*lastPartialLength) {
            /* do we have enough data to fill out the partial word? */
            if (csumlen >= (sizeof(unsigned long) - *lastPartialLength)) { /* YES, we do... */
                memcpy(((char *) &temp + *lastPartialLength), src,
                       (sizeof(unsigned long) - *lastPartialLength));
                src = (unsigned long *) ((char *) src + sizeof(unsigned long) - *lastPartialLength);
                csum += (temp - *lastPartialLong);
                csumlen -= sizeof(unsigned long) - *lastPartialLength;
                /* now we have a source of unknown alignment */
                if (WORDALIGNED(src)) {
                    for (i = 0; i < csumlen / sizeof(unsigned long); i++) {
                        csum += *src++;
                    }
                    csumlen -= i * sizeof(unsigned long);
                    *lastPartialLong = 0;
                    *lastPartialLength = 0;
                } else {
                    for (i = 0; i < csumlen / sizeof(unsigned long); i++) {
                        memcpy(&temp, src, sizeof(temp));
                        csum += temp;
                        src++;
                    }
                    csumlen -= i * sizeof(unsigned long);
                    *lastPartialLong = 0;
                    *lastPartialLength = 0;
                }
            } else { /* NO, we don't... */
                memcpy(((char *) &temp + *lastPartialLength), src, csumlen);
                src = (unsigned long *) ((char *) src + csumlen);
                csum += (temp - *lastPartialLong);
                *lastPartialLong = temp;
                *lastPartialLength += csumlen;
                csumlen = 0;
            }
        } else {
            for (; csumlen >= sizeof(*src); csumlen -= sizeof(*src)) {
                memcpy(&temp, src, sizeof(temp));
                src++;
                csum += temp;
            }
            *lastPartialLength = 0;
            *lastPartialLong = 0;
        }
    }

    /* if csumlen is non-zero there was a bit left, less than an unsigned long's worth */
    if (csumlen != 0) {
        temp = *lastPartialLong;
        if (*lastPartialLength) {
            if (csumlen >= (sizeof(unsigned long) - *lastPartialLength)) {
                /* fill out rest of partial word and add to checksum */
                memcpy(((char *) &temp + *lastPartialLength), src,
                       (sizeof(unsigned long) - *lastPartialLength));
                csum += (temp - *lastPartialLong);
                csumlen -= sizeof(unsigned long) - *lastPartialLength;
                src = (unsigned long *) ((char *) src + sizeof(unsigned long) - *lastPartialLength);
                *lastPartialLength = csumlen;
                /* reset temp, and calculate next partial word */
                temp = 0;
                if (csumlen) {
                    memcpy(&temp, src, csumlen);
                }
                /* add it to the the checksum */
                csum += temp;
                *lastPartialLong = temp;
            } else {
                /* fill out rest of partial word and add to checksum */
                memcpy(((char *) &temp + *lastPartialLength), src, csumlen);
                csum += (temp - *lastPartialLong);
                *lastPartialLong = temp;
                *lastPartialLength += csumlen;
            }
        } else { /* fast path... */
            /* temp and *lastPartialLong are 0 if *lastPartialLength is 0... */
            memcpy(&temp, src, csumlen);
            csum += temp;
            *lastPartialLong = temp;
            *lastPartialLength = csumlen;
            /* done...return the checksum */
        }
    }

    return csum;
}

unsigned int opal_uicsum_partial(const void *source, size_t csumlen, unsigned int *lastPartialInt,
                                 size_t *lastPartialLength)
{
    unsigned int *src = (unsigned int *) source;
    unsigned int csum = 0;
    unsigned int temp;
    unsigned long i;

    temp = *lastPartialInt;

    if (INTALIGNED(source)) {
        if (*lastPartialLength) {
            /* do we have enough data to fill out the partial word? */
            if (csumlen >= (sizeof(unsigned int) - *lastPartialLength)) { /* YES, we do... */
                memcpy(((char *) &temp + *lastPartialLength), src,
                       (sizeof(unsigned int) - *lastPartialLength));
                src = (unsigned int *) ((char *) src + sizeof(unsigned int) - *lastPartialLength);
                csum += (temp - *lastPartialInt);
                csumlen -= sizeof(unsigned int) - *lastPartialLength;
                /* now we have an unaligned source */
                for (i = 0; i < csumlen / sizeof(unsigned int); i++) {
                    memcpy(&temp, src, sizeof(temp));
                    csum += temp;
                    src++;
                }
                csumlen -= i * sizeof(unsigned int);
                *lastPartialInt = 0;
                *lastPartialLength = 0;
            } else { /* NO, we don't... */
                memcpy(((char *) &temp + *lastPartialLength), src, csumlen);
                src = (unsigned int *) ((char *) src + csumlen);
                csum += (temp - *lastPartialInt);
                *lastPartialInt = temp;
                *lastPartialLength += csumlen;
                csumlen = 0;
            }
        } else { /* fast path... */
            size_t numLongs = csumlen / sizeof(unsigned int);
            for (i = 0; i < numLongs; i++) {
                csum += *src++;
            }
            *lastPartialInt = 0;
            *lastPartialLength = 0;
            if (INTALIGNED(csumlen)) {
                return (csum);
            } else {
                csumlen -= i * sizeof(unsigned int);
            }
        }
    } else {
        if (*lastPartialLength) {
            /* do we have enough data to fill out the partial word? */
            if (csumlen >= (sizeof(unsigned int) - *lastPartialLength)) { /* YES, we do... */
                memcpy(((char *) &temp + *lastPartialLength), src,
                       (sizeof(unsigned int) - *lastPartialLength));
                src = (unsigned int *) ((char *) src + sizeof(unsigned int) - *lastPartialLength);
                csum += (temp - *lastPartialInt);
                csumlen -= sizeof(unsigned int) - *lastPartialLength;
                /* now we have a source of unknown alignment */
                if (INTALIGNED(src)) {
                    for (i = 0; i < csumlen / sizeof(unsigned int); i++) {
                        csum += *src++;
                    }
                    csumlen -= i * sizeof(unsigned int);
                    *lastPartialInt = 0;
                    *lastPartialLength = 0;
                } else {
                    for (i = 0; i < csumlen / sizeof(unsigned int); i++) {
                        memcpy(&temp, src, sizeof(temp));
                        csum += temp;
                        src++;
                    }
                    csumlen -= i * sizeof(unsigned int);
                    *lastPartialInt = 0;
                    *lastPartialLength = 0;
                }
            } else { /* NO, we don't... */
                memcpy(((char *) &temp + *lastPartialLength), src, csumlen);
                src = (unsigned int *) ((char *) src + csumlen);
                csum += (temp - *lastPartialInt);
                *lastPartialInt = temp;
                *lastPartialLength += csumlen;
                csumlen = 0;
            }
        } else {
            for (; csumlen >= sizeof(*src); csumlen -= sizeof(*src)) {
                memcpy(&temp, src, sizeof(temp));
                src++;
                csum += temp;
            }
            *lastPartialLength = 0;
            *lastPartialInt = 0;
        }
    }

    /* if csumlen is non-zero there was a bit left, less than an unsigned int's worth */
    if (csumlen != 0) {
        temp = *lastPartialInt;
        if (*lastPartialLength) {
            if (csumlen >= (sizeof(unsigned int) - *lastPartialLength)) {
                /* fill out rest of partial word and add to checksum */
                memcpy(((char *) &temp + *lastPartialLength), src,
                       (sizeof(unsigned int) - *lastPartialLength));
                csum += (temp - *lastPartialInt);
                csumlen -= sizeof(unsigned int) - *lastPartialLength;
                src = (unsigned int *) ((char *) src + sizeof(unsigned int) - *lastPartialLength);
                *lastPartialLength = csumlen;
                /* reset temp, and calculate next partial word */
                temp = 0;
                if (csumlen) {
                    memcpy(&temp, src, csumlen);
                }
                /* add it to the the checksum */
                csum += temp;
                *lastPartialInt = temp;
            } else {
                /* fill out rest of partial word and add to checksum */
                memcpy(((char *) &temp + *lastPartialLength), src, csumlen);
                csum += (temp - *lastPartialInt);
                *lastPartialInt = temp;
                *lastPartialLength += csumlen;
            }
        } else { /* fast path... */
            /* temp and *lastPartialInt are 0 if *lastPartialLength is 0... */
            memcpy(&temp, src, csumlen);
            csum += temp;
            *lastPartialInt = temp;
            *lastPartialLength = csumlen;
            /* done...return the checksum */
        }
    }

    return csum;
}

/* globals for CRC32 bcopy and calculation routines */

static bool _opal_crc_table_initialized = false;
static unsigned int _opal_crc_table[256];

/* CRC32 table generation routine - thanks to Charles Michael Heard for his
 * optimized CRC32 code...
 */

void opal_initialize_crc_table(void)
{
    register int i, j;
    register unsigned int crc_accum;

    for (i = 0; i < 256; i++) {
        crc_accum = (i << 24);
        for (j = 0; j < 8; j++) {
            if (crc_accum & 0x80000000) {
                crc_accum = (crc_accum << 1) ^ CRC_POLYNOMIAL;
            } else {
                crc_accum = (crc_accum << 1);
            }
        }
        _opal_crc_table[i] = crc_accum;
    }

    /* set global bool to true to do this work once! */
    _opal_crc_table_initialized = true;
    return;
}

unsigned int opal_bcopy_uicrc_partial(const void *source, void *destination, size_t copylen,
                                      size_t crclen, unsigned int partial_crc)
{
    size_t crclenresidue = (crclen > copylen) ? (crclen - copylen) : 0;
    register int i, j;
    register unsigned char t;
    unsigned int tmp;

    if (!_opal_crc_table_initialized) {
        opal_initialize_crc_table();
    }

    if (INTALIGNED(source) && INTALIGNED(destination)) {
        register unsigned int *src = (unsigned int *) source;
        register unsigned int *dst = (unsigned int *) destination;
        register unsigned char *ts, *td;
        /* copy whole integers */
        while (copylen >= sizeof(unsigned int)) {
            tmp = *src++;
            *dst++ = tmp;
            ts = (unsigned char *) &tmp;
            for (j = 0; j < (int) sizeof(unsigned int); j++) {
                i = ((partial_crc >> 24) ^ *ts++) & 0xff;
                partial_crc = (partial_crc << 8) ^ _opal_crc_table[i];
            }
            copylen -= sizeof(unsigned int);
        }
        ts = (unsigned char *) src;
        td = (unsigned char *) dst;
        /* copy partial integer */
        while (copylen--) {
            t = *ts++;
            *td++ = t;
            i = ((partial_crc >> 24) ^ t) & 0xff;
            partial_crc = (partial_crc << 8) ^ _opal_crc_table[i];
        }
        /* calculate CRC over remaining bytes... */
        while (crclenresidue--) {
            i = ((partial_crc >> 24) ^ *ts++) & 0xff;
            partial_crc = (partial_crc << 8) ^ _opal_crc_table[i];
        }
    } else {
        register unsigned char *src = (unsigned char *) source;
        register unsigned char *dst = (unsigned char *) destination;
        while (copylen--) {
            t = *src++;
            *dst++ = t;
            i = ((partial_crc >> 24) ^ t) & 0xff;
            partial_crc = (partial_crc << 8) ^ _opal_crc_table[i];
        }
        while (crclenresidue--) {
            i = ((partial_crc >> 24) ^ *src++) & 0xff;
            partial_crc = (partial_crc << 8) ^ _opal_crc_table[i];
        }
    }

    return partial_crc;
}

unsigned int opal_uicrc_partial(const void *source, size_t crclen, unsigned int partial_crc)
{
    register int i, j;
    register unsigned char *t;
    unsigned int tmp;

    if (!_opal_crc_table_initialized) {
        opal_initialize_crc_table();
    }

    if (INTALIGNED(source)) {
        register unsigned int *src = (unsigned int *) source;
        while (crclen >= sizeof(unsigned int)) {
            tmp = *src++;
            t = (unsigned char *) &tmp;
            for (j = 0; j < (int) sizeof(unsigned int); j++) {
                i = ((partial_crc >> 24) ^ *t++) & 0xff;
                partial_crc = (partial_crc << 8) ^ _opal_crc_table[i];
            }
            crclen -= sizeof(unsigned int);
        }
        t = (unsigned char *) src;
        while (crclen--) {
            i = ((partial_crc >> 24) ^ *t++) & 0xff;
            partial_crc = (partial_crc << 8) ^ _opal_crc_table[i];
        }
    } else {
        register unsigned char *src = (unsigned char *) source;
        while (crclen--) {
            i = ((partial_crc >> 24) ^ *src++) & 0xff;
            partial_crc = (partial_crc << 8) ^ _opal_crc_table[i];
        }
    }

    return partial_crc;
}
