From 5b7fd2451cd356ca224fe771be8b07200cf468e0 Mon Sep 17 00:00:00 2001 From: "darin%netscape.com" Date: Sat, 3 Mar 2001 01:57:37 +0000 Subject: [PATCH] Bug 70707. Pulled generic transport implementation out of new memory cache and created netwerk/base/src/nsStorageTransport.{h,cpp} --- netwerk/base/src/Makefile.in | 1 + netwerk/base/src/makefile.win | 1 + netwerk/base/src/nsStorageTransport.cpp | 858 ++++++++++++++++++++++++ netwerk/base/src/nsStorageTransport.h | 202 ++++++ 4 files changed, 1062 insertions(+) create mode 100644 netwerk/base/src/nsStorageTransport.cpp create mode 100644 netwerk/base/src/nsStorageTransport.h diff --git a/netwerk/base/src/Makefile.in b/netwerk/base/src/Makefile.in index add6237074ad..d850b72553cf 100644 --- a/netwerk/base/src/Makefile.in +++ b/netwerk/base/src/Makefile.in @@ -59,6 +59,7 @@ CPPSRCS = \ nsProtocolProxyService.cpp \ nsProxyAutoConfigUtils.cpp \ nsAsyncStreamListener.cpp \ + nsStorageTransport.cpp \ $(NULL) # we don't want the shared lib, but we want to force the creation of a diff --git a/netwerk/base/src/makefile.win b/netwerk/base/src/makefile.win index 4c3b6a974299..bb355fa732d9 100644 --- a/netwerk/base/src/makefile.win +++ b/netwerk/base/src/makefile.win @@ -55,6 +55,7 @@ CPP_OBJS = \ .\$(OBJDIR)\nsProtocolProxyService.obj \ .\$(OBJDIR)\nsProxyAutoConfigUtils.obj \ .\$(OBJDIR)\nsAsyncStreamListener.obj \ + .\$(OBJDIR)\nsStorageTransport.obj \ $(NULL) INCS = $(INCS) \ diff --git a/netwerk/base/src/nsStorageTransport.cpp b/netwerk/base/src/nsStorageTransport.cpp new file mode 100644 index 000000000000..6449877abab5 --- /dev/null +++ b/netwerk/base/src/nsStorageTransport.cpp @@ -0,0 +1,858 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla 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/MPL/ + * + * 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 nsStorageTransport.cpp, released February 26, 2001. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 2001 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#include "nsStorageTransport.h" +#include "nsIProxyObjectManager.h" +#include "nsIServiceManager.h" +#include "nsCRT.h" +#include "prmem.h" +#include "netCore.h" + +#define MAX_IO_CHUNK 8192 // maximum count reported per OnDataAvailable +#define MAX_COUNT ((PRUint32) -1) + +static NS_DEFINE_CID(kProxyObjectManagerCID, NS_PROXYEVENT_MANAGER_CID); + +//---------------------------------------------------------------------------- +// helper functions... +//---------------------------------------------------------------------------- + +static NS_METHOD +nsWriteToBuffer(nsIInputStream *aInput, + void *aClosure, + const char *aFromBuf, + PRUint32 aOffset, + PRUint32 aCount, + PRUint32 *aWriteCount) +{ + char *toBuf = NS_REINTERPRET_CAST(char *, aClosure); + nsCRT::memcpy(toBuf + aOffset, aFromBuf, aCount); + *aWriteCount = aCount; + return NS_OK; +} + +static NS_METHOD +nsReadFromBuffer(nsIOutputStream *aOutput, + void *aClosure, + char *aToBuf, + PRUint32 aOffset, + PRUint32 aCount, + PRUint32 *aReadCount) +{ + const char *fromBuf = NS_REINTERPRET_CAST(const char *, aClosure); + nsCRT::memcpy(aToBuf, fromBuf + aOffset, aCount); + *aReadCount = aCount; + return NS_OK; +} + +static NS_METHOD +nsReadFromInputStream(nsIOutputStream *aOutput, + void *aClosure, + char *aToBuf, + PRUint32 aOffset, + PRUint32 aCount, + PRUint32 *aReadCount) +{ + nsIInputStream *fromStream = NS_REINTERPRET_CAST(nsIInputStream *, aClosure); + return fromStream->Read(aToBuf, aCount, aReadCount); +} + +//---------------------------------------------------------------------------- +// nsStorageTransport +//---------------------------------------------------------------------------- + +nsStorageTransport::nsStorageTransport() + : mOutputStream(nsnull) + , mSegmentSize(DEFAULT_SEGMENT_SIZE) + , mMaxSize(DEFAULT_BUFFER_SIZE) + , mSegments(nsnull) + , mWriteSegment(nsnull) + , mWriteCursor(0) +{ + NS_INIT_ISUPPORTS(); + + PR_INIT_CLIST(&mReadRequests); + PR_INIT_CLIST(&mInputStreams); +} + +nsStorageTransport::~nsStorageTransport() +{ + if (mOutputStream) + CloseOutputStream(); + + DeleteAllSegments(); +} + +nsresult +nsStorageTransport::Init(PRUint32 aBufSegmentSize, PRUint32 aBufMaxSize) +{ + mSegmentSize = aBufSegmentSize; + mMaxSize = aBufMaxSize; + return NS_OK; +} + +nsresult +nsStorageTransport::GetReadSegment(PRUint32 aOffset, char **aPtr, PRUint32 *aCount) +{ + *aPtr = nsnull; + *aCount = 0; + + if (aOffset < mWriteCursor) { + PRUint32 index = aOffset / mSegmentSize; + nsSegment *s = GetNthSegment(index); + if (s) { + PRUint32 offset = aOffset % mSegmentSize; + *aPtr = s->Data() + offset; + *aCount = mSegmentSize - offset; + } + } + return NS_OK; +} + +nsresult +nsStorageTransport::GetWriteSegment(char **aPtr, PRUint32 *aCount) +{ + NS_ENSURE_ARG_POINTER(aPtr); + NS_ENSURE_ARG_POINTER(aCount); + + if (mWriteSegment) { + PRUint32 offset = mWriteCursor % mSegmentSize; + *aPtr = mWriteSegment->Data() + offset; + *aCount = mSegmentSize - offset; + return NS_OK; + } + else { + // add a new segment for writing and redo... + nsresult rv = AddWriteSegment(); + return NS_FAILED(rv) ? rv : GetWriteSegment(aPtr, aCount); + } +} + +nsresult +nsStorageTransport::AddToBytesWritten(PRUint32 aCount) +{ + // advance write cursor + mWriteCursor += aCount; + + // clear write segment if we have written to the end + if (!(mWriteCursor % mSegmentSize)) + mWriteSegment = nsnull; + + // process waiting readers + PRCList *link = PR_LIST_HEAD(&mReadRequests); + for (; link != &mReadRequests; link = PR_NEXT_LINK(link)) { + nsReadRequest *req = NS_STATIC_CAST(nsReadRequest *, link); + if (req->IsWaitingForWrite()) + req->Process(); + } + + return NS_OK; +} + +nsresult +nsStorageTransport::CloseOutputStream() +{ + if (mOutputStream) { + mOutputStream->SetTransport(nsnull); + mOutputStream = nsnull; + + // XXX wake up blocked reads + } + return NS_OK; +} + +nsresult +nsStorageTransport::ReadRequestCompleted(nsReadRequest *aReader) +{ + // remove the reader from the list of readers + PR_REMOVE_AND_INIT_LINK(aReader); + aReader->SetTransport(nsnull); + return NS_OK; +} + +nsresult +nsStorageTransport::Available(PRUint32 aStartingFrom, PRUint32 *aCount) +{ + *aCount = mWriteCursor - aStartingFrom; + return NS_OK; +} + +nsresult +nsStorageTransport::AddWriteSegment() +{ + NS_ASSERTION(mWriteSegment == nsnull, "write segment is non-null"); + + mWriteSegment = (nsSegment *) PR_Malloc(sizeof(nsSegment) + mSegmentSize); + if (!mWriteSegment) + return NS_ERROR_OUT_OF_MEMORY; + + mWriteSegment->next = nsnull; + + AppendSegment(mWriteSegment); + return NS_OK; +} + +void +nsStorageTransport::AppendSegment(nsSegment *aSegment) +{ + if (!mSegments) + mSegments = aSegment; + else { + nsSegment *s = mSegments; + for (; s && s->next; s = s->next); + s->next = aSegment; + } +} + +void +nsStorageTransport::DeleteSegments(nsSegment *segments) +{ + while (segments) { + nsSegment *s = segments->next; + PR_Free(segments); + segments = s; + } +} + +void +nsStorageTransport::TruncateTo(PRUint32 aOffset) +{ + if (aOffset < mWriteCursor) { + if (aOffset == 0) { + DeleteSegments(mSegments); + mSegments = nsnull; + mWriteSegment = nsnull; + } + else { + PRUint32 offset = 0; + nsSegment *s = mSegments; + for (; s; s = s->next) { + if ((offset + mSegmentSize) > aOffset) + break; + offset += mSegmentSize; + } + // "s" now points to the last segment that we should keep + if (s->next) { + DeleteSegments(s->next); + s->next = nsnull; + } + mWriteSegment = s; + } + } + mWriteCursor = aOffset; +} + +nsStorageTransport::nsSegment * +nsStorageTransport::GetNthSegment(PRUint32 index) +{ + nsSegment *s = mSegments; + for (; s && index; s = s->next, --index); + return s; +} + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsStorageTransport, + nsITransport) + +NS_IMETHODIMP +nsStorageTransport::GetSecurityInfo(nsISupports **aSecurityInfo) +{ + NS_ENSURE_ARG_POINTER(aSecurityInfo); + *aSecurityInfo = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsStorageTransport::GetProgressEventSink(nsIProgressEventSink **aSink) +{ + NS_ENSURE_ARG_POINTER(aSink); + *aSink = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsStorageTransport::SetProgressEventSink(nsIProgressEventSink *aSink) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsStorageTransport::OpenInputStream(PRUint32 aOffset, + PRUint32 aCount, + PRUint32 aFlags, + nsIInputStream **aInput) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsStorageTransport::OpenOutputStream(PRUint32 aOffset, + PRUint32 aCount, + PRUint32 aFlags, + nsIOutputStream **aOutput) +{ + NS_ENSURE_TRUE(!mOutputStream, NS_ERROR_IN_PROGRESS); + + if (!PR_CLIST_IS_EMPTY(&mInputStreams) || !PR_CLIST_IS_EMPTY(&mReadRequests)) { + NS_NOTREACHED("Attempt to open a memory cache output stream while " + "read is in progress!"); + return NS_ERROR_FAILURE; + } + + NS_NEWXPCOM(mOutputStream, nsOutputStream); + if (!mOutputStream) + return NS_ERROR_OUT_OF_MEMORY; + + mOutputStream->SetTransport(this); + mOutputStream->SetTransferCount(aCount); + + TruncateTo(aOffset); + + NS_ADDREF(*aOutput = mOutputStream); + return NS_OK; +} + +NS_IMETHODIMP +nsStorageTransport::AsyncRead(nsIStreamListener *aListener, + nsISupports *aContext, + PRUint32 aOffset, + PRUint32 aCount, + PRUint32 aFlags, + nsIRequest **aRequest) +{ + nsresult rv = NS_OK; + + nsReadRequest *reader; + NS_NEWXPCOM(reader, nsReadRequest); + if (!reader) + return NS_ERROR_OUT_OF_MEMORY; + + reader->SetTransport(this); + reader->SetTransferOffset(aOffset); + reader->SetTransferCount(aCount); + + // append the read request to the list of existing read requests. + // it is important to do this before the possibility of failure. + PR_APPEND_LINK(reader, &mReadRequests); + + rv = reader->SetListener(aListener, aContext); + if (NS_FAILED(rv)) goto error; + + rv = reader->Process(); + if (NS_FAILED(rv)) goto error; + + NS_ADDREF(*aRequest = reader); + return NS_OK; + +error: + NS_DELETEXPCOM(reader); + return rv; +} + +NS_IMETHODIMP +nsStorageTransport::AsyncWrite(nsIStreamProvider *aProvider, + nsISupports *aContext, + PRUint32 aOffset, + PRUint32 aCount, + PRUint32 aFlags, + nsIRequest **aRequest) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +//---------------------------------------------------------------------------- +// nsStorageTransport::nsReadRequest +//---------------------------------------------------------------------------- + +nsStorageTransport::nsReadRequest::nsReadRequest() + : mTransport(nsnull) + , mTransferOffset(0) + , mTransferCount(MAX_COUNT) + , mStatus(NS_OK) + , mCanceled(PR_FALSE) + , mOnStartFired(PR_FALSE) + , mWaitingForWrite(PR_FALSE) +{ + NS_INIT_ISUPPORTS(); + PR_INIT_CLIST(this); +} + +nsStorageTransport::nsReadRequest::~nsReadRequest() +{ + if (mTransport) + mTransport->ReadRequestCompleted(this); +} + +nsresult +nsStorageTransport::nsReadRequest::SetListener(nsIStreamListener *aListener, + nsISupports *aListenerContext) +{ + nsresult rv = NS_OK; + + mListener = aListener; + mListenerContext = aListenerContext; + + // We proxy listener events to ourself and then forward them onto + // the real listener (on the listener's thread). + + nsCOMPtr proxyMgr = + do_GetService(kProxyObjectManagerCID, &rv); + + if (NS_SUCCEEDED(rv)) + rv = proxyMgr->GetProxyForObject(NS_CURRENT_EVENTQ, + NS_GET_IID(nsIStreamListener), + NS_STATIC_CAST(nsIStreamListener *, this), + PROXY_ASYNC | PROXY_ALWAYS, + getter_AddRefs(mListenerProxy)); + return rv; +} + +nsresult +nsStorageTransport::nsReadRequest::Process() +{ + nsresult rv = NS_OK; + + // this method must always be called on the client's thread + + NS_ENSURE_TRUE(mTransport, NS_ERROR_NOT_INITIALIZED); + + // always clear this flag initially + mWaitingForWrite = PR_FALSE; + + PRUint32 count = 0; + + rv = mTransport->Available(mTransferOffset, &count); + if (NS_FAILED(rv)) return rv; + + if (!mOnStartFired) { + // no need to proxy this callback + (void) mListener->OnStartRequest(this, mListenerContext); + mOnStartFired = PR_TRUE; + } + + count = PR_MIN(count, mTransferCount); + + if (count) { + count = PR_MIN(count, MAX_IO_CHUNK); + + // proxy this callback + (void) mListenerProxy->OnDataAvailable(this, mListenerContext, + this, + mTransferOffset, + count); + } + else if ((mTransferCount == 0) || !mTransport->HasWriter()) { + + // there is no more data to read and there is no writer, so we + // must stop this read request. + + // first let the transport know that we are done + mTransport->ReadRequestCompleted(this); + + // no need to proxy this callback + (void) mListener->OnStopRequest(this, mListenerContext, mStatus, nsnull); + } + else + mWaitingForWrite = PR_TRUE; + + return rv; +} + +NS_IMPL_THREADSAFE_ISUPPORTS5(nsStorageTransport::nsReadRequest, + nsITransportRequest, + nsIRequest, + nsIStreamListener, + nsIStreamObserver, + nsIInputStream) + +NS_IMETHODIMP +nsStorageTransport::nsReadRequest::GetTransport(nsITransport **aTransport) +{ + NS_ENSURE_ARG_POINTER(aTransport); + NS_IF_ADDREF(*aTransport = mTransport); + return NS_OK; +} + +NS_IMETHODIMP +nsStorageTransport::nsReadRequest::GetName(PRUnichar **aName) +{ + NS_ENSURE_ARG_POINTER(aName); + *aName = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsStorageTransport::nsReadRequest::IsPending(PRBool *aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + *aResult = (mTransport ? PR_TRUE : PR_FALSE); + return NS_OK; +} + +NS_IMETHODIMP +nsStorageTransport::nsReadRequest::GetStatus(nsresult *aStatus) +{ + NS_ENSURE_ARG_POINTER(aStatus); + *aStatus = mStatus; + return NS_OK; +} + +NS_IMETHODIMP +nsStorageTransport::nsReadRequest::Cancel(nsresult aStatus) +{ + mCanceled = PR_TRUE; + mStatus = aStatus; + return NS_OK; +} + +NS_IMETHODIMP +nsStorageTransport::nsReadRequest::Suspend() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsStorageTransport::nsReadRequest::Resume() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsStorageTransport::nsReadRequest::OnStartRequest(nsIRequest *aRequest, + nsISupports *aContext) +{ + NS_NOTREACHED("nsStorageTransport::nsReadRequest::OnStartRequest"); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsStorageTransport::nsReadRequest::OnStopRequest(nsIRequest *aRequest, + nsISupports *aContext, + nsresult aStatus, + const PRUnichar *aStatusText) +{ + NS_NOTREACHED("nsStorageTransport::nsReadRequest::OnStopRequest"); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsStorageTransport::nsReadRequest::OnDataAvailable(nsIRequest *aRequest, + nsISupports *aContext, + nsIInputStream *aInput, + PRUint32 aOffset, + PRUint32 aCount) +{ + nsresult rv = NS_OK; + + rv = mListener->OnDataAvailable(aRequest, aContext, aInput, aOffset, aCount); + + NS_ASSERTION(rv != NS_BASE_STREAM_WOULD_BLOCK, "not implemented"); + + if (NS_FAILED(rv)) return rv; + + // post the next message... + return Process(); +} + +NS_IMETHODIMP +nsStorageTransport::nsReadRequest::Close() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsStorageTransport::nsReadRequest::Available(PRUint32 *aCount) +{ + NS_ENSURE_TRUE(mTransport, NS_BASE_STREAM_CLOSED); + return mTransport->Available(mTransferOffset, aCount); +} + +NS_IMETHODIMP +nsStorageTransport::nsReadRequest::Read(char *aBuf, PRUint32 aCount, PRUint32 *aBytesRead) +{ + return ReadSegments(nsWriteToBuffer, aBuf, aCount, aBytesRead); +} + +NS_IMETHODIMP +nsStorageTransport::nsReadRequest::ReadSegments(nsWriteSegmentFun aWriter, + void *aClosure, + PRUint32 aCount, + PRUint32 *aBytesRead) +{ + NS_ENSURE_TRUE(mTransport, NS_BASE_STREAM_CLOSED); + + nsresult rv = NS_OK; + + *aBytesRead = 0; + + // limit the number of bytes that can be read + aCount = PR_MIN(aCount, mTransferCount); + + while (aCount) { + char *ptr = nsnull; + PRUint32 count = 0; + + rv = mTransport->GetReadSegment(mTransferOffset, &ptr, &count); + if (NS_FAILED(rv)) return rv; + + count = PR_MIN(count, aCount); + + while (count) { + PRUint32 writeCount = 0; + + rv = aWriter(this, aClosure, ptr, *aBytesRead, count, &writeCount); + + if (rv == NS_BASE_STREAM_WOULD_BLOCK) + return NS_OK; // mask this error + else if (NS_FAILED(rv)) + return rv; + + ptr += writeCount; + count -= writeCount; + aCount -= writeCount; + *aBytesRead += writeCount; + + // decrement the total number of bytes remaining to be read + mTransferCount -= writeCount; + mTransferOffset += writeCount; + } + } + return rv; +} + +NS_IMETHODIMP +nsStorageTransport::nsReadRequest::GetNonBlocking(PRBool *aNonBlocking) +{ + NS_ENSURE_ARG_POINTER(aNonBlocking); + *aNonBlocking = PR_FALSE; + return NS_OK; +} + +NS_IMETHODIMP +nsStorageTransport::nsReadRequest::GetObserver(nsIInputStreamObserver **aObserver) +{ + NS_ENSURE_ARG_POINTER(aObserver); + *aObserver = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsStorageTransport::nsReadRequest::SetObserver(nsIInputStreamObserver *aObserver) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +//---------------------------------------------------------------------------- +// nsStorageTransport::nsBlockingStream +//---------------------------------------------------------------------------- + +nsStorageTransport::nsBlockingStream::nsBlockingStream() + : mTransport(nsnull) + , mTransferCount(MAX_COUNT) +{ +} + +nsStorageTransport::nsBlockingStream::~nsBlockingStream() +{ +} + +//---------------------------------------------------------------------------- +// nsStorageTransport::nsInputStream +//---------------------------------------------------------------------------- + +nsStorageTransport::nsInputStream::nsInputStream() + : mOffset(0) +{ + NS_INIT_ISUPPORTS(); +} + +nsStorageTransport::nsInputStream::~nsInputStream() +{ +} + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsStorageTransport::nsInputStream, + nsIInputStream) + +NS_IMETHODIMP +nsStorageTransport::nsInputStream::Close() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsStorageTransport::nsInputStream::Available(PRUint32 *aCount) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsStorageTransport::nsInputStream::Read(char *aBuf, PRUint32 aCount, PRUint32 *aBytesRead) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsStorageTransport::nsInputStream::ReadSegments(nsWriteSegmentFun aWriter, + void *aClosure, + PRUint32 aCount, + PRUint32 *aBytesRead) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsStorageTransport::nsInputStream::GetNonBlocking(PRBool *aNonBlocking) +{ + NS_ENSURE_ARG_POINTER(aNonBlocking); + *aNonBlocking = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +nsStorageTransport::nsInputStream::GetObserver(nsIInputStreamObserver **aObserver) +{ + NS_ENSURE_ARG_POINTER(aObserver); + *aObserver = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsStorageTransport::nsInputStream::SetObserver(nsIInputStreamObserver *aObserver) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +//---------------------------------------------------------------------------- +// nsStorageTransport::nsOutputStream +//---------------------------------------------------------------------------- + +nsStorageTransport::nsOutputStream::nsOutputStream() +{ + NS_INIT_ISUPPORTS(); +} + +nsStorageTransport::nsOutputStream::~nsOutputStream() +{ + if (mTransport) + mTransport->CloseOutputStream(); +} + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsStorageTransport::nsOutputStream, + nsIOutputStream) + +NS_IMETHODIMP +nsStorageTransport::nsOutputStream::Close() +{ + NS_ENSURE_TRUE(mTransport, NS_BASE_STREAM_CLOSED); + return mTransport->CloseOutputStream(); +} + +NS_IMETHODIMP +nsStorageTransport::nsOutputStream::Flush() +{ + return NS_OK; +} + +NS_IMETHODIMP +nsStorageTransport::nsOutputStream::Write(const char *aBuf, + PRUint32 aCount, + PRUint32 *aBytesWritten) +{ + return WriteSegments(nsReadFromBuffer, (void *) aBuf, aCount, aBytesWritten); +} + +NS_IMETHODIMP +nsStorageTransport::nsOutputStream::WriteFrom(nsIInputStream *aInput, + PRUint32 aCount, + PRUint32 *aBytesWritten) +{ + return WriteSegments(nsReadFromInputStream, aInput, aCount, aBytesWritten); +} + +NS_IMETHODIMP +nsStorageTransport::nsOutputStream::WriteSegments(nsReadSegmentFun aReader, + void *aClosure, + PRUint32 aCount, + PRUint32 *aBytesWritten) +{ + NS_ENSURE_TRUE(mTransport, NS_BASE_STREAM_CLOSED); + + nsresult rv = NS_OK; + + *aBytesWritten = 0; + + // XXX need to honor mTransferCount + + while (aCount) { + char *ptr; + PRUint32 count; + + rv = mTransport->GetWriteSegment(&ptr, &count); + if (NS_FAILED(rv)) return rv; + + count = PR_MIN(count, aCount); + + while (count) { + PRUint32 readCount; + + rv = aReader(this, aClosure, ptr, *aBytesWritten, count, &readCount); + + if (NS_FAILED(rv)) + break; + + count -= readCount; + aCount -= readCount; + *aBytesWritten += readCount; + + rv = mTransport->AddToBytesWritten(readCount); + } + } + return rv; +} + +NS_IMETHODIMP +nsStorageTransport::nsOutputStream::GetNonBlocking(PRBool *aNonBlocking) +{ + NS_ENSURE_ARG_POINTER(aNonBlocking); + *aNonBlocking = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +nsStorageTransport::nsOutputStream::SetNonBlocking(PRBool aNonBlocking) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsStorageTransport::nsOutputStream::GetObserver(nsIOutputStreamObserver **aObserver) +{ + NS_ENSURE_ARG_POINTER(aObserver); + *aObserver = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsStorageTransport::nsOutputStream::SetObserver(nsIOutputStreamObserver *aObserver) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} diff --git a/netwerk/base/src/nsStorageTransport.h b/netwerk/base/src/nsStorageTransport.h new file mode 100644 index 000000000000..624c93be0ebf --- /dev/null +++ b/netwerk/base/src/nsStorageTransport.h @@ -0,0 +1,202 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla 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/MPL/ + * + * 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 nsStorageTransport.h, released February 26, 2001. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 2001 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#ifndef nsStorageTransport_h__ +#define nsStorageTransport_h__ + +#include "nsITransport.h" +#include "nsIInputStream.h" +#include "nsIOutputStream.h" +#include "nsIStreamListener.h" +#include "prclist.h" +#include "nsCOMPtr.h" + +/** + * Each "stream-based" memory cache entry has one transport + * associated with it. The transport supports multiple + * simultaneous read requests and a single write request. + */ +class nsStorageTransport : public nsITransport +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSITRANSPORT + + enum { DEFAULT_SEGMENT_SIZE = 1024 }; + enum { DEFAULT_BUFFER_SIZE = 1024 * 1024 }; + + nsStorageTransport(); + virtual ~nsStorageTransport(); + + nsresult Init(PRUint32 aSegmentSize = DEFAULT_SEGMENT_SIZE, + PRUint32 aBufferSize = DEFAULT_BUFFER_SIZE); + +private: + + /** + * The transport request object returned by AsyncRead + */ + class nsReadRequest : public PRCList + , public nsITransportRequest + , public nsIStreamListener + , public nsIInputStream + { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSITRANSPORTREQUEST + NS_DECL_NSIREQUEST + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSISTREAMOBSERVER + NS_DECL_NSIINPUTSTREAM + + nsReadRequest(); + virtual ~nsReadRequest(); + + void SetTransport(nsStorageTransport *t) { mTransport = t; } + void SetTransferOffset(PRUint32 o) { mTransferOffset = o; } + void SetTransferCount(PRUint32 c) { mTransferCount = c; } + + nsresult SetListener(nsIStreamListener *, nsISupports *); + nsresult Process(); + + PRBool IsWaitingForWrite() { return mWaitingForWrite; } + + private: + nsStorageTransport *mTransport; // weak ref + PRUint32 mTransferOffset; + PRUint32 mTransferCount; + nsresult mStatus; + nsCOMPtr mListenerProxy; + nsCOMPtr mListener; + nsCOMPtr mListenerContext; + PRPackedBool mCanceled; + PRPackedBool mOnStartFired; + PRPackedBool mWaitingForWrite; + }; + + /** + * A base class for the blocking streams + */ + class nsBlockingStream + { + public: + nsBlockingStream(); + virtual ~nsBlockingStream(); + + void SetTransport(nsStorageTransport *t) { mTransport = t; } + void SetTransferCount(PRUint32 c) { mTransferCount = c; } + + protected: + nsStorageTransport *mTransport; // weak ref + PRUint32 mTransferCount; + }; + + /** + * The blocking input stream object returned by OpenInputStream + */ + class nsInputStream : public PRCList + , public nsBlockingStream + , public nsIInputStream + { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIINPUTSTREAM + + nsInputStream(); + virtual ~nsInputStream(); + + private: + PRUint32 mOffset; + }; + + /** + * The blocking output stream object returned by OpenOutputStream + */ + class nsOutputStream : public nsBlockingStream + , public nsIOutputStream + { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOUTPUTSTREAM + + nsOutputStream(); + virtual ~nsOutputStream(); + }; + + friend class nsReadRequest; + friend class nsBlockingStream; + friend class nsInputStream; + friend class nsOutputStream; + + /** + * Methods called from the friend classes + */ + nsresult GetReadSegment(PRUint32 aOffset, char **aPtr, PRUint32 *aCount); + nsresult GetWriteSegment(char **aPtr, PRUint32 *aCount); + nsresult AddToBytesWritten(PRUint32 aCount); + nsresult CloseOutputStream(); + nsresult ReadRequestCompleted(nsReadRequest *); + nsresult Available(PRUint32 aStartingFrom, PRUint32 *aCount); + + PRBool HasWriter() { return (mOutputStream != nsnull); } + +private: + + /** + * Data is stored in a singly-linked list of segments + */ + struct nsSegment + { + struct nsSegment *next; + + char *Data() { return NS_REINTERPRET_CAST(char *, this) + sizeof(*this); } + // the data follows this structure (the two are allocated together) + }; + + /** + * Internal methods + */ + nsresult AddWriteSegment(); + + void AppendSegment(nsSegment *); + void DeleteSegments(nsSegment *); + void DeleteAllSegments() { TruncateTo(0); } + void TruncateTo(PRUint32 aOffset); + + nsSegment *GetNthSegment(PRUint32 aIndex); + +private: + nsOutputStream *mOutputStream; // weak ref + PRCList mInputStreams; // weak ref to objects + PRCList mReadRequests; // weak ref to objects + + PRUint32 mSegmentSize; + PRUint32 mMaxSize; + + nsSegment *mSegments; + + nsSegment *mWriteSegment; + PRUint32 mWriteCursor; +}; + +#endif