/*
 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 */

#include "jni.h"
#include "jni_util.h"
#include "jlong.h"
#include <string.h>

#include "JNIHelp.h"

/*
 * WARNING:
 *
 * Do not replace instances of:
 *
 *   if (length > MBYTE)
 *     size = MBYTE;
 *   else
 *     size = length;
 *
 * with
 *
 *   size = (length > MBYTE ? MBYTE : length);
 *
 * This expression causes a c compiler assertion failure when compiling on
 * 32-bit sparc.
 */

#define MBYTE 1048576

#define GETCRITICAL(bytes, env, obj) { \
    bytes = (*env)->GetPrimitiveArrayCritical(env, obj, NULL); \
    if (bytes == NULL) \
        JNU_ThrowInternalError(env, "Unable to get array"); \
}

#define RELEASECRITICAL(bytes, env, obj, mode) { \
    (*env)->ReleasePrimitiveArrayCritical(env, obj, bytes, mode); \
}

#define SWAPSHORT(x) ((jshort)(((x) << 8) | (((x) >> 8) & 0xff)))
#define SWAPINT(x)   ((jint)((SWAPSHORT((jshort)(x)) << 16) | \
                            (SWAPSHORT((jshort)((x) >> 16)) & 0xffff)))
#define SWAPLONG(x)  ((jlong)(((jlong)SWAPINT((jint)(x)) << 32) | \
                              ((jlong)SWAPINT((jint)((x) >> 32)) & 0xffffffff)))

#define NATIVE_METHOD(className, functionName, signature) \
{ #functionName, signature, (void*)(Java_java_nio_ ## className ## _ ## functionName) }

JNIEXPORT void JNICALL
Java_java_nio_Bits_copyFromShortArray(JNIEnv *env, jobject this, jobject src,
                                      jlong srcPos, jlong dstAddr, jlong length)
{
    jbyte *bytes;
    size_t size;
    jshort *srcShort, *dstShort, *endShort;
    jshort tmpShort;

    dstShort = (jshort *)jlong_to_ptr(dstAddr);

    while (length > 0) {
        /* do not change this if-else statement, see WARNING above */
        if (length > MBYTE)
            size = MBYTE;
        else
            size = (size_t)length;

        GETCRITICAL(bytes, env, src);

        srcShort = (jshort *)(bytes + srcPos);
        endShort = srcShort + (size / sizeof(jshort));
        while (srcShort < endShort) {
          tmpShort = *srcShort++;
          *dstShort++ = SWAPSHORT(tmpShort);
        }

        RELEASECRITICAL(bytes, env, src, JNI_ABORT);

        length -= size;
        dstAddr += size;
        srcPos += size;
    }
}

JNIEXPORT void JNICALL
Java_java_nio_Bits_copyToShortArray(JNIEnv *env, jobject this, jlong srcAddr,
                                    jobject dst, jlong dstPos, jlong length)
{
    jbyte *bytes;
    size_t size;
    jshort *srcShort, *dstShort, *endShort;
    jshort tmpShort;

    srcShort = (jshort *)jlong_to_ptr(srcAddr);

    while (length > 0) {
        /* do not change this if-else statement, see WARNING above */
        if (length > MBYTE)
            size = MBYTE;
        else
            size = (size_t)length;

        GETCRITICAL(bytes, env, dst);

        dstShort = (jshort *)(bytes + dstPos);
        endShort = srcShort + (size / sizeof(jshort));
        while (srcShort < endShort) {
            tmpShort = *srcShort++;
            *dstShort++ = SWAPSHORT(tmpShort);
        }

        RELEASECRITICAL(bytes, env, dst, 0);

        length -= size;
        srcAddr += size;
        dstPos += size;
    }
}

JNIEXPORT void JNICALL
Java_java_nio_Bits_copyFromIntArray(JNIEnv *env, jobject this, jobject src,
                                    jlong srcPos, jlong dstAddr, jlong length)
{
    jbyte *bytes;
    size_t size;
    jint *srcInt, *dstInt, *endInt;
    jint tmpInt;

    dstInt = (jint *)jlong_to_ptr(dstAddr);

    while (length > 0) {
        /* do not change this code, see WARNING above */
        if (length > MBYTE)
            size = MBYTE;
        else
            size = (size_t)length;

        GETCRITICAL(bytes, env, src);

        srcInt = (jint *)(bytes + srcPos);
        endInt = srcInt + (size / sizeof(jint));
        while (srcInt < endInt) {
            tmpInt = *srcInt++;
            *dstInt++ = SWAPINT(tmpInt);
        }

        RELEASECRITICAL(bytes, env, src, JNI_ABORT);

        length -= size;
        dstAddr += size;
        srcPos += size;
    }
}

JNIEXPORT void JNICALL
Java_java_nio_Bits_copyToIntArray(JNIEnv *env, jobject this, jlong srcAddr,
                                  jobject dst, jlong dstPos, jlong length)
{
    jbyte *bytes;
    size_t size;
    jint *srcInt, *dstInt, *endInt;
    jint tmpInt;

    srcInt = (jint *)jlong_to_ptr(srcAddr);

    while (length > 0) {
        /* do not change this code, see WARNING above */
        if (length > MBYTE)
            size = MBYTE;
        else
            size = (size_t)length;

        GETCRITICAL(bytes, env, dst);

        dstInt = (jint *)(bytes + dstPos);
        endInt = srcInt + (size / sizeof(jint));
        while (srcInt < endInt) {
            tmpInt = *srcInt++;
            *dstInt++ = SWAPINT(tmpInt);
        }

        RELEASECRITICAL(bytes, env, dst, 0);

        length -= size;
        srcAddr += size;
        dstPos += size;
    }
}

JNIEXPORT void JNICALL
Java_java_nio_Bits_copyFromLongArray(JNIEnv *env, jobject this, jobject src,
                                     jlong srcPos, jlong dstAddr, jlong length)
{
    jbyte *bytes;
    size_t size;
    jlong *srcLong, *dstLong, *endLong;
    jlong tmpLong;

    dstLong = (jlong *)jlong_to_ptr(dstAddr);

    while (length > 0) {
        /* do not change this code, see WARNING above */
        if (length > MBYTE)
            size = MBYTE;
        else
            size = (size_t)length;

        GETCRITICAL(bytes, env, src);

        srcLong = (jlong *)(bytes + srcPos);
        endLong = srcLong + (size / sizeof(jlong));
        while (srcLong < endLong) {
            tmpLong = *srcLong++;
            *dstLong++ = SWAPLONG(tmpLong);
        }

        RELEASECRITICAL(bytes, env, src, JNI_ABORT);

        length -= size;
        dstAddr += size;
        srcPos += size;
    }
}

JNIEXPORT void JNICALL
Java_java_nio_Bits_copyToLongArray(JNIEnv *env, jobject this, jlong srcAddr,
                                   jobject dst, jlong dstPos, jlong length)
{
    jbyte *bytes;
    size_t size;
    jlong *srcLong, *dstLong, *endLong;
    jlong tmpLong;

    srcLong = (jlong *)jlong_to_ptr(srcAddr);

    while (length > 0) {
        /* do not change this code, see WARNING above */
        if (length > MBYTE)
            size = MBYTE;
        else
            size = (size_t)length;

        GETCRITICAL(bytes, env, dst);

        dstLong = (jlong *)(bytes + dstPos);
        endLong = srcLong + (size / sizeof(jlong));
        while (srcLong < endLong) {
            tmpLong = *srcLong++;
            *dstLong++ = SWAPLONG(tmpLong);
        }

        RELEASECRITICAL(bytes, env, dst, 0);

        length -= size;
        srcAddr += size;
        dstPos += size;
    }
}

static JNINativeMethod gMethods[] = {
    NATIVE_METHOD(Bits, copyFromShortArray, "(Ljava/lang/Object;JJJ)V"),
    NATIVE_METHOD(Bits, copyToShortArray, "(JLjava/lang/Object;JJ)V"),
    NATIVE_METHOD(Bits, copyFromIntArray, "(Ljava/lang/Object;JJJ)V"),
    NATIVE_METHOD(Bits, copyToIntArray, "(JLjava/lang/Object;JJ)V"),
    NATIVE_METHOD(Bits, copyFromLongArray, "(Ljava/lang/Object;JJJ)V"),
    NATIVE_METHOD(Bits, copyToLongArray, "(JLjava/lang/Object;JJ)V"),
};

void register_java_nio_Bits(JNIEnv* env) {
    jniRegisterNativeMethods(env, "java/nio/Bits", gMethods, NELEM(gMethods));
}
