/*
 * @(#)SocketInputStream.c	1.20 98/09/21
 *
 * Copyright 1997, 1998 by Sun Microsystems, Inc.,
 * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
 * All rights reserved.
 * 
 * This software is the confidential and proprietary information
 * of Sun Microsystems, Inc. ("Confidential Information").  You
 * shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement
 * you entered into with Sun.
 */

#include "jvm_utils.h"
#include "java_net_SocketInputStream.h"


/************************************************************************
 * SocketInputStream
 */

static jfieldID IO_fd_fdID;

static jfieldID sis_fdID;
static jfieldID sis_implID;

/*
 * Class:     java_net_SocketInputStream
 * Method:    init
 * Signature: ()V
 */
JNIEXPORT void JNICALL 
Java_java_net_SocketInputStream_init(JNIEnv *env, jclass cls) {

    sis_fdID = (*env)->GetFieldID(env, cls, "fd", 
				  "Ljava/io/FileDescriptor;");
    IO_fd_fdID = NET_GetFileDescriptorID(env);
    sis_implID = (*env)->GetFieldID(env, cls, "impl", 
				    "Ljava/net/SocketImpl;");
}

/*
 * Class:     java_net_SocketInputStream
 * Method:    socketRead
 * Signature: ([BII)I
 */
JNIEXPORT jint JNICALL
Java_java_net_SocketInputStream_socketRead(JNIEnv *env, jobject this, 
					   jbyteArray data, jint off, jint len)
{
    char BUF[MAX_BUFFER_LEN];

    /* The fd field */
    jobject fdObj = (*env)->GetObjectField(env, this, sis_fdID);
    jint fd, timeout, nread;

    /* The impl field */
    jobject impl = (*env)->GetObjectField(env, this, sis_implID);

    jint datalen;

    if (IS_NULL(fdObj)) {
	/* should't this be a NullPointerException? -br */
        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 
			"null fd object");
	return -1;
    } else {
        fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
    }
    if (IS_NULL(data)) {
	JNU_ThrowNullPointerException(env, "data argument");
        return -1;
    }
    if (IS_NULL(impl)) {
	JNU_ThrowNullPointerException(env, "socket impl");
	return -1;
    } else {
	timeout = (*env)->GetIntField(env, impl, psi_timeoutID);
    }

    datalen = (*env)->GetArrayLength(env, data);

    if (len < 0 || len + off > datalen) {
        JNU_ThrowByName(env, JNU_JAVAPKG "ArrayIndexOutOfBoundsException", 0);
	return -1;
    }

    if (len == 0) {
	return 0;
    } else if (len > MAX_BUFFER_LEN) {

	/* REMIND: if requested amount to be read is > MAX_BUFFER_LEN
	 * we're still only reading MAX_BUFFER_LEN.  The calling level
	 * will have to do multiple reads.  This is technically still
	 * correct, but this may break someone.  But it allows us to
	 * use a static buffer on the stack which is faster than from
	 * the malloc heap or java heap.  
	 */
    	len = MAX_BUFFER_LEN;
    } 

    if (timeout) {
	nread = JVM_Timeout(fd, timeout);
        if (nread == 0) {
            JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
			    "Read timed out");
            return -1;
        } else if (nread == JVM_IO_ERR) {
	    JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
			    (errno == EBADF) ? "Socket closed" :
			    strerror(errno));
            return -1;
	} else if (nread == JVM_IO_INTR) {
	    JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
			    "Operation interrupted");
            return -1;
	}
    }
    nread = JVM_Read(fd, BUF, len);
    if (nread < 0) {
	NET_ThrowCurrent(env, strerror(errno));
	return -1;
    }
    (*env)->SetByteArrayRegion(env, data, off, nread, (jbyte *)BUF);

    return nread;
}					   
