gecko-dev/java/webclient/src_moz/InputStreamShim.cpp

553 строки
15 KiB
C++

/* -*- 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"
#include "ns_globals.h"
#include "nsIEventTarget.h"
#include "nsStreamUtils.h"
static const PRInt32 buffer_increment = 5120;
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),
mCloseStatus(NS_OK), mCallback(nsnull), mCallbackFlags(0)
{
NS_INIT_ISUPPORTS();
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;
PR_Lock(mLock);
delete [] mBuffer;
mBuffer = nsnull;
mBufferLength = 0;
PR_Unlock(mLock);
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_INTERFACE2(InputStreamShim,
nsIInputStream,
nsIAsyncInputStream)
nsresult InputStreamShim::doReadFromJava()
{
nsresult rv = NS_ERROR_FAILURE;
PR_ASSERT(mLock);
PR_LOG(prLogModuleInfo, PR_LOG_DEBUG,
("InputStreamShim::doReadFromJava: entering\n"));
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 (mAvailable <= 0 ||
(0 != mCountFromJava &&
(((PRUint32)mContentLength) == mCountFromJava))) {
mDoClose = PR_TRUE;
rv = doClose();
if (0 == mAvailableForMozilla) {
rv = NS_ERROR_NOT_AVAILABLE;
}
goto DRFJ_CLEANUP;
}
if (NS_FAILED(doRead())) {
rv = NS_ERROR_FAILURE;
goto DRFJ_CLEANUP;
}
rv = NS_OK;
// if we have bytes available for mozilla, and they it has requested
// a callback when bytes are available.
if (mCallback && 0 < mAvailableForMozilla
&& !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
rv = mCallback->OnInputStreamReady(this);
mCallback = nsnull;
mCallbackFlags = nsnull;
if (NS_FAILED(rv)) {
goto DRFJ_CLEANUP;
}
}
// finally, do another check for available bytes
if (NS_FAILED(doAvailable())) {
rv = NS_ERROR_FAILURE;
goto DRFJ_CLEANUP;
}
if (mAvailable <= 0 ||
(0 != mCountFromJava &&
(((PRUint32)mContentLength) == mCountFromJava))) {
mDoClose = PR_TRUE;
rv = doClose();
if (0 == mAvailableForMozilla) {
rv = NS_ERROR_NOT_AVAILABLE;
}
goto DRFJ_CLEANUP;
}
DRFJ_CLEANUP:
PR_Unlock(mLock);
PR_LOG(prLogModuleInfo, PR_LOG_DEBUG,
("InputStreamShim::doReadFromJava: exiting\n"));
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 = (PRInt32) 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_LOG(prLogModuleInfo, PR_LOG_DEBUG,
("InputStreamShim::doRead: entering\n"));
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
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
PR_LOG(prLogModuleInfo, PR_LOG_DEBUG,
("InputStreamShim::doRead: exiting\n"));
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
if (mCallback && mAvailableForMozilla &&
!(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
rv = mCallback->OnInputStreamReady(this);
mCallback = nsnull;
mCallbackFlags = nsnull;
}
mCloseStatus = NS_BASE_STREAM_CLOSED;
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;
}
if (mDoClose) {
if (mAvailableForMozilla) {
*aResult = mAvailableForMozilla;
return NS_OK;
}
return mCloseStatus;
}
PR_ASSERT(mLock);
PR_Lock(mLock);
// *aResult = mAvailableForMozilla;
*aResult = PR_UINT32_MAX;
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;
mCloseStatus = NS_BASE_STREAM_CLOSED;
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;
}
PR_LOG(prLogModuleInfo, PR_LOG_DEBUG,
("InputStreamShim::Read: entering\n"));
if (mDoClose) {
PR_LOG(prLogModuleInfo, PR_LOG_DEBUG,
("InputStreamShim::Read: exiting, already closed\n"));
return mCloseStatus;
}
*aNumRead = 0;
PR_ASSERT(mLock);
PR_ASSERT(mCountFromMozilla <= mCountFromJava);
PR_Lock(mLock);
if (mAvailableForMozilla) {
if (aCount <= (mCountFromJava - mCountFromMozilla)) {
// what she's asking for is less than or equal to what we have
memcpy(aBuffer, (mBuffer + mCountFromMozilla), aCount);
mCountFromMozilla += aCount;
*aNumRead = aCount;
}
else {
// what she's asking for is more than what we have
memcpy(aBuffer, (mBuffer + mCountFromMozilla),
(mCountFromJava - mCountFromMozilla));
*aNumRead = (mCountFromJava - mCountFromMozilla);
mCountFromMozilla += (mCountFromJava - mCountFromMozilla);
}
mAvailableForMozilla -= *aNumRead;
rv = NS_OK;
}
else {
PR_LOG(prLogModuleInfo, PR_LOG_DEBUG,
("InputStreamShim::Read: exiting, would block\n"));
rv = NS_BASE_STREAM_WOULD_BLOCK;
}
PR_Unlock(mLock);
PR_LOG(prLogModuleInfo, PR_LOG_DEBUG,
("InputStreamShim::Read: exiting\n"));
return rv;
}
NS_IMETHODIMP
InputStreamShim::ReadSegments(nsWriteSegmentFun writer, void * aClosure,
PRUint32 aCount, PRUint32 *aNumRead)
{
nsresult rv = NS_ERROR_FAILURE;
if (!writer || !aNumRead) {
return NS_ERROR_NULL_POINTER;
}
PR_LOG(prLogModuleInfo, PR_LOG_DEBUG,
("InputStreamShim::ReadSegments: entering\n"));
if (mDoClose && 0 == mAvailableForMozilla) {
PR_LOG(prLogModuleInfo, PR_LOG_DEBUG,
("InputStreamShim::ReadSegments: exiting, already closed\n"));
return mCloseStatus;
}
*aNumRead = 0;
PR_ASSERT(mLock);
PR_ASSERT(mCountFromMozilla <= mCountFromJava);
PR_Lock(mLock);
PRInt32 bytesToWrite = mAvailableForMozilla;
PRUint32 bytesWritten;
PRUint32 totalBytesWritten = 0;
if (bytesToWrite > aCount) {
bytesToWrite = aCount;
}
if (0 == mAvailableForMozilla) {
PR_LOG(prLogModuleInfo, PR_LOG_DEBUG,
("InputStreamShim::Read: exiting, would block\n"));
rv = NS_BASE_STREAM_WOULD_BLOCK;
}
while (bytesToWrite) {
// what she's asking for is less than or equal to what we have
rv = writer(this, aClosure,
(mBuffer + mCountFromMozilla),
totalBytesWritten, bytesToWrite, &bytesWritten);
if (NS_FAILED(rv)) {
break;
}
bytesToWrite -= bytesWritten;
totalBytesWritten += bytesWritten;
mCountFromMozilla += bytesWritten;
}
*aNumRead = totalBytesWritten;
mAvailableForMozilla -= totalBytesWritten;
PR_Unlock(mLock);
PR_LOG(prLogModuleInfo, PR_LOG_DEBUG,
("InputStreamShim::ReadSegments: exiting\n"));
}
NS_IMETHODIMP
InputStreamShim::IsNonBlocking(PRBool *_retval)
{
*_retval = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
InputStreamShim::CloseWithStatus(nsresult astatus)
{
this->Close();
mCloseStatus = astatus;
return NS_OK;
}
NS_IMETHODIMP
InputStreamShim::AsyncWait(nsIInputStreamCallback *aCallback,
PRUint32 aFlags, PRUint32 aRequestedCount,
nsIEventTarget *aEventTarget)
{
PR_Lock(mLock);
mCallback = nsnull;
mCallbackFlags = nsnull;
nsCOMPtr<nsIInputStreamCallback> proxy;
if (aEventTarget) {
nsresult rv = NS_NewInputStreamReadyEvent(getter_AddRefs(proxy),
aCallback, aEventTarget);
if (NS_FAILED(rv)) return rv;
aCallback = proxy;
}
if (NS_FAILED(mCloseStatus) ||
(mAvailableForMozilla && !(aFlags & WAIT_CLOSURE_ONLY))) {
// stream is already closed or readable; post event.
aCallback->OnInputStreamReady(this);
}
else {
// queue up callback object to be notified when data becomes available
mCallback = aCallback;
mCallbackFlags = aFlags;
}
PR_Unlock(mLock);
return NS_OK;
}