/*
 * Copyright (c) 1996, 2011, 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 "jvm.h"
#include "jni_util.h"
#include "jlong.h"

#include <nativehelper/JNIHelp.h>

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



/*
 * Class:     java_io_ObjectInputStream
 * Method:    bytesToFloats
 * Signature: ([BI[FII)V
 *
 * Reconstitutes nfloats float values from their byte representations.  Byte
 * values are read from array src starting at offset srcpos; the resulting
 * float values are written to array dst starting at dstpos.
 */
JNIEXPORT void JNICALL
Java_java_io_ObjectInputStream_bytesToFloats(JNIEnv *env,
                                             jclass this,
                                             jbyteArray src,
                                             jint srcpos,
                                             jfloatArray dst,
                                             jint dstpos,
                                             jint nfloats)
{
    union {
        int i;
        float f;
    } u;
    jfloat *floats;
    jbyte *bytes;
    jsize dstend;
    jint ival;

    if (nfloats == 0)
        return;

    /* fetch source array */
    if (src == NULL) {
        JNU_ThrowNullPointerException(env, NULL);
        return;
    }
    bytes = (*env)->GetPrimitiveArrayCritical(env, src, NULL);
    if (bytes == NULL)          /* exception thrown */
        return;

    /* fetch dest array */
    if (dst == NULL) {
        (*env)->ReleasePrimitiveArrayCritical(env, src, bytes, JNI_ABORT);
        JNU_ThrowNullPointerException(env, NULL);
        return;
    }
    floats = (*env)->GetPrimitiveArrayCritical(env, dst, NULL);
    if (floats == NULL) {       /* exception thrown */
        (*env)->ReleasePrimitiveArrayCritical(env, src, bytes, JNI_ABORT);
        return;
    }

    /* do conversion */
    dstend = dstpos + nfloats;
    for ( ; dstpos < dstend; dstpos++) {
        ival = ((bytes[srcpos + 0] & 0xFF) << 24) +
               ((bytes[srcpos + 1] & 0xFF) << 16) +
               ((bytes[srcpos + 2] & 0xFF) << 8) +
               ((bytes[srcpos + 3] & 0xFF) << 0);
        u.i = (long) ival;
        floats[dstpos] = (jfloat) u.f;
        srcpos += 4;
    }

    (*env)->ReleasePrimitiveArrayCritical(env, src, bytes, JNI_ABORT);
    (*env)->ReleasePrimitiveArrayCritical(env, dst, floats, 0);
}

/*
 * Class:     java_io_ObjectInputStream
 * Method:    bytesToDoubles
 * Signature: ([BI[DII)V
 *
 * Reconstitutes ndoubles double values from their byte representations.
 * Byte values are read from array src starting at offset srcpos; the
 * resulting double values are written to array dst starting at dstpos.
 */
JNIEXPORT void JNICALL
Java_java_io_ObjectInputStream_bytesToDoubles(JNIEnv *env,
                                              jclass this,
                                              jbyteArray src,
                                              jint srcpos,
                                              jdoubleArray dst,
                                              jint dstpos,
                                              jint ndoubles)

{
    union {
        jlong l;
        double d;
    } u;
    jdouble *doubles;
    jbyte *bytes;
    jsize dstend;
    jlong lval;

    if (ndoubles == 0)
        return;

    /* fetch source array */
    if (src == NULL) {
        JNU_ThrowNullPointerException(env, NULL);
        return;
    }
    bytes = (*env)->GetPrimitiveArrayCritical(env, src, NULL);
    if (bytes == NULL)          /* exception thrown */
        return;

    /* fetch dest array */
    if (dst == NULL) {
        (*env)->ReleasePrimitiveArrayCritical(env, src, bytes, JNI_ABORT);
        JNU_ThrowNullPointerException(env, NULL);
        return;
    }
    doubles = (*env)->GetPrimitiveArrayCritical(env, dst, NULL);
    if (doubles == NULL) {      /* exception thrown */
        (*env)->ReleasePrimitiveArrayCritical(env, src, bytes, JNI_ABORT);
        return;
    }

    /* do conversion */
    dstend = dstpos + ndoubles;
    for ( ; dstpos < dstend; dstpos++) {
        lval = (((jlong) bytes[srcpos + 0] & 0xFF) << 56) +
               (((jlong) bytes[srcpos + 1] & 0xFF) << 48) +
               (((jlong) bytes[srcpos + 2] & 0xFF) << 40) +
               (((jlong) bytes[srcpos + 3] & 0xFF) << 32) +
               (((jlong) bytes[srcpos + 4] & 0xFF) << 24) +
               (((jlong) bytes[srcpos + 5] & 0xFF) << 16) +
               (((jlong) bytes[srcpos + 6] & 0xFF) << 8) +
               (((jlong) bytes[srcpos + 7] & 0xFF) << 0);
        jlong_to_jdouble_bits(&lval);
        u.l = lval;
        doubles[dstpos] = (jdouble) u.d;
        srcpos += 8;
    }

    (*env)->ReleasePrimitiveArrayCritical(env, src, bytes, JNI_ABORT);
    (*env)->ReleasePrimitiveArrayCritical(env, dst, doubles, 0);
}

static JNINativeMethod gMethods[] = {
    NATIVE_METHOD(ObjectInputStream, bytesToFloats, "([BI[FII)V"),
    NATIVE_METHOD(ObjectInputStream, bytesToDoubles, "([BI[DII)V"),
};

void register_java_io_ObjectInputStream(JNIEnv* env) {
    jniRegisterNativeMethods(env, "java/io/ObjectInputStream", gMethods, NELEM(gMethods));
}
