/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998-1999 Netscape Communications Corporation. All * Rights Reserved. * */ #include "InputStreamShim.h" #include "jni_util.h" #include "nsCRT.h" #include "prlock.h" #include "prlog.h" #include "prthread.h" static const PRInt32 buffer_increment = 1024; static const PRInt32 do_close_code = -524; InputStreamShim::InputStreamShim(jobject yourJavaStreamRef, PRInt32 yourContentLength) : mJavaStream(yourJavaStreamRef), mContentLength(yourContentLength), mBuffer(nsnull), mBufferLength(0), mCountFromJava(0), mCountFromMozilla(0), mAvailable(0), mAvailableForMozilla(0), mNumRead(0), mDoClose(PR_FALSE), mDidClose(PR_FALSE), mLock(nsnull) { NS_INIT_REFCNT(); mLock = PR_NewLock(); JNIEnv *env = (JNIEnv *) JNU_GetEnv(gVm, JNI_VERSION); } InputStreamShim::~InputStreamShim() { JNIEnv *env = (JNIEnv *) JNU_GetEnv(gVm, JNI_VERSION); ::util_DeleteGlobalRef(env, mJavaStream); mJavaStream = nsnull; mContentLength = -1; delete [] mBuffer; mBuffer = nsnull; mBufferLength = 0; mAvailable = 0; mAvailableForMozilla = 0; mNumRead = 0; mDoClose = PR_TRUE; mDidClose = PR_TRUE; PR_DestroyLock(mLock); mLock = nsnull; } //NS_IMPL_ISUPPORTS(InputStreamShim, NS_GET_IID(nsIInputStream)) NS_IMETHODIMP_(nsrefcnt) InputStreamShim::AddRef(void) { NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt"); // Note that we intentionally don't check for owning thread safety. ++mRefCnt; NS_LOG_ADDREF(this, mRefCnt, "InputStreamShim", sizeof(*this)); return mRefCnt; } NS_IMETHODIMP_(nsrefcnt) InputStreamShim::Release(void) { NS_PRECONDITION(0 != mRefCnt, "dup release"); // Note that we intentionally don't check for owning thread safety. --mRefCnt; NS_LOG_RELEASE(this, mRefCnt, "InputStreamShim"); if (mRefCnt == 0) { mRefCnt = 1; /* stabilize */ NS_DELETEXPCOM(this); return 0; } return mRefCnt; } NS_IMPL_QUERY_INTERFACE(InputStreamShim, NS_GET_IID(nsIInputStream)) nsresult InputStreamShim::doReadFromJava() { nsresult rv = NS_ERROR_FAILURE; PR_ASSERT(mLock); PR_Lock(mLock); // first, see how much data is available if (NS_FAILED(rv = doAvailable())) { goto DRFJ_CLEANUP; } // if we have all our data, give the error appropriate result if (0 == mAvailable || (0 != mCountFromJava && (((PRUint32)mContentLength) == mCountFromJava))) { mDoClose = PR_TRUE; doClose(); rv = NS_ERROR_NOT_AVAILABLE; goto DRFJ_CLEANUP; } if (NS_FAILED(doRead())) { rv = NS_ERROR_FAILURE; goto DRFJ_CLEANUP; } rv = NS_OK; // finally, do another check for available bytes if (NS_FAILED(doAvailable())) { rv = NS_ERROR_FAILURE; goto DRFJ_CLEANUP; } if (0 == mAvailable || (0 != mCountFromJava && (((PRUint32)mContentLength) == mCountFromJava))) { mDoClose = PR_TRUE; doClose(); rv = NS_ERROR_NOT_AVAILABLE; goto DRFJ_CLEANUP; } DRFJ_CLEANUP: PR_Unlock(mLock); return rv; } // // Helper methods called from doReadFromJava // nsresult InputStreamShim::doAvailable(void) { nsresult rv = NS_ERROR_FAILURE; if (mDidClose) { mAvailable = 0; return NS_OK; } #ifdef BAL_INTERFACE #else JNIEnv *env = (JNIEnv *) JNU_GetEnv(gVm, JNI_VERSION); jclass streamClass = nsnull; jmethodID mid = nsnull; if (!(streamClass = env->GetObjectClass(mJavaStream))) { return rv; } if (!(mid = env->GetMethodID(streamClass, "available", "()I"))) { return rv; } mAvailable = (PRUint32) env->CallIntMethod(mJavaStream, mid); if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear(); return NS_ERROR_FAILURE; } rv = NS_OK; #endif return rv; } nsresult InputStreamShim::doRead(void) { nsresult rv = NS_ERROR_FAILURE; if (mDoClose) { doClose(); return NS_ERROR_NOT_AVAILABLE; } PR_ASSERT(0 != mAvailable); // if we don't have a buffer, create one if (!mBuffer) { if (0 < mContentLength) { mBuffer = new char[mContentLength]; mBufferLength = mContentLength; } else { // make sure we allocate enough buffer to store what is // currently available. if (mAvailable < buffer_increment) { mBufferLength = buffer_increment; } else { PRInt32 bufLengthCalc = mAvailable / buffer_increment; mBufferLength = buffer_increment + (bufLengthCalc * buffer_increment); } mBuffer = new char[mBufferLength]; } if (!mBuffer) { mBufferLength = 0; return NS_ERROR_FAILURE; } } else { // See if we need to grow our buffer. If what we have plus what // we're about to get is greater than the current buffer size... if (mBufferLength < (mCountFromJava + mAvailable)) { // create the new buffer char *tBuffer = new char[mBufferLength + buffer_increment]; if (!tBuffer) { return NS_ERROR_FAILURE; } // copy the old buffer into the new buffer nsCRT::memcpy(tBuffer, mBuffer, mBufferLength); // delete the old buffer delete [] mBuffer; // update mBuffer; mBuffer = tBuffer; // update our bufferLength mBufferLength += buffer_increment; } } #ifdef BAL_INTERFACE #else JNIEnv *env = (JNIEnv *) JNU_GetEnv(gVm, JNI_VERSION); jmethodID mid = nsnull; jclass streamClass = nsnull; jbyteArray javaByteArray = nsnull; if (!(streamClass = env->GetObjectClass(mJavaStream))) { return rv; } if (!(mid = env->GetMethodID(streamClass, "read", "([BII)I"))) { return rv; } if (!(javaByteArray = env->NewByteArray((jsize) mAvailable))) { return rv; } mNumRead = env->CallIntMethod(mJavaStream, mid, javaByteArray, (jint) 0, (jint) mAvailable); if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear(); return NS_ERROR_FAILURE; } if (0 < mNumRead) { // copy the bytes from java into our buffer env->GetByteArrayRegion(javaByteArray, 0, (jint) mNumRead, (jbyte *) (mBuffer + mCountFromJava)); mCountFromJava += mNumRead; mAvailableForMozilla = mCountFromJava - mCountFromMozilla; } rv = NS_OK; #endif return rv; } nsresult InputStreamShim::doClose(void) { nsresult rv = NS_ERROR_FAILURE; PR_ASSERT(mDoClose); if (mDidClose) { return NS_OK; } #ifdef BAL_INTERFACE #else JNIEnv *env = (JNIEnv *) JNU_GetEnv(gVm, JNI_VERSION); jclass streamClass = nsnull; jmethodID mid = nsnull; if (!(streamClass = env->GetObjectClass(mJavaStream))) { return rv; } if (!(mid = env->GetMethodID(streamClass, "close", "()V"))) { return rv; } env->CallVoidMethod(mJavaStream, mid); if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear(); return NS_ERROR_FAILURE; } rv = NS_OK; #endif mDidClose = PR_TRUE; return rv; } // // nsIInputStream methods // NS_IMETHODIMP InputStreamShim::Available(PRUint32* aResult) { nsresult rv = NS_ERROR_FAILURE; if (!aResult) { return NS_ERROR_NULL_POINTER; } PR_ASSERT(mLock); PR_Lock(mLock); *aResult = mAvailableForMozilla; rv = NS_OK; PR_Unlock(mLock); return rv; } NS_IMETHODIMP InputStreamShim::Close() { nsresult rv = NS_ERROR_FAILURE; PR_ASSERT(mLock); PR_Lock(mLock); mDoClose = PR_TRUE; rv = NS_OK; PR_Unlock(mLock); return rv; } NS_IMETHODIMP InputStreamShim::Read(char* aBuffer, PRUint32 aCount, PRUint32 *aNumRead) { nsresult rv = NS_ERROR_FAILURE; if (!aBuffer || !aNumRead) { return NS_ERROR_NULL_POINTER; } *aNumRead = 0; PR_ASSERT(mLock); PR_ASSERT(mCountFromMozilla <= mCountFromJava); PR_Lock(mLock); // wait for java to load the buffer with some data do { if (mAvailableForMozilla == 0) { PR_Unlock(mLock); PR_Sleep(PR_INTERVAL_MIN); PR_Lock(mLock); } else { break; } } while ((!mDidClose) && (-1 != mNumRead)); if (mAvailableForMozilla) { if (aCount <= (mCountFromJava - mCountFromMozilla)) { // what she's asking for is less than or equal to what we have nsCRT::memcpy(aBuffer, (mBuffer + mCountFromMozilla), aCount); mCountFromMozilla += aCount; *aNumRead = aCount; } else { // what she's asking for is more than what we have nsCRT::memcpy(aBuffer, (mBuffer + mCountFromMozilla), (mCountFromJava - mCountFromMozilla)); *aNumRead = (mCountFromJava - mCountFromMozilla); mCountFromMozilla += (mCountFromJava - mCountFromMozilla); } mAvailableForMozilla -= *aNumRead; } rv = NS_OK; PR_Unlock(mLock); return rv; } NS_IMETHODIMP InputStreamShim::ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 count, PRUint32 *_retval) { NS_NOTREACHED("ReadSegments"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP InputStreamShim::GetNonBlocking(PRBool *aNonBlocking) { NS_NOTREACHED("GetNonBlocking"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP InputStreamShim::GetObserver(nsIInputStreamObserver * *aObserver) { NS_NOTREACHED("GetObserver"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP InputStreamShim::SetObserver(nsIInputStreamObserver * aObserver) { NS_NOTREACHED("SetObserver"); return NS_ERROR_NOT_IMPLEMENTED; }