mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-05 08:35:26 +00:00
810 lines
26 KiB
C++
810 lines
26 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
#include "nsIPipe.h"
|
|
#include "nsIBufferInputStream.h"
|
|
#include "nsIBufferOutputStream.h"
|
|
#include "nsSegmentedBuffer.h"
|
|
#include "nsAutoLock.h"
|
|
#include "nsIServiceManager.h"
|
|
#ifdef PAGE_MANAGER
|
|
#include "nsIPageManager.h"
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
class nsPipe : public nsIPipe
|
|
{
|
|
public:
|
|
// We can't inherit from both nsIBufferInputStream and nsIBufferOutputStream
|
|
// because they collide on their Close method. Consequently we nest their
|
|
// implementations to avoid the extra object allocation, and speed up method
|
|
// invocation between them and the nsPipe's buffer manipulation methods.
|
|
|
|
class nsPipeInputStream : public nsIBufferInputStream {
|
|
public:
|
|
NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
|
|
NS_IMETHOD_(nsrefcnt) AddRef(void);
|
|
NS_IMETHOD_(nsrefcnt) Release(void);
|
|
// nsIBaseStream methods:
|
|
NS_IMETHOD Close(void);
|
|
// nsIInputStream methods:
|
|
NS_IMETHOD Available(PRUint32 *result);
|
|
NS_IMETHOD Read(char* toBuf, PRUint32 bufLen, PRUint32 *readCount);
|
|
// nsIBufferInputStream methods:
|
|
NS_IMETHOD GetBuffer(nsIBuffer * *aBuffer) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
NS_IMETHOD ReadSegments(nsWriteSegmentFun writer, void* closure, PRUint32 count,
|
|
PRUint32 *readCount);
|
|
NS_IMETHOD Search(const char *forString, PRBool ignoreCase, PRBool *found,
|
|
PRUint32 *offsetSearchedTo);
|
|
NS_IMETHOD GetNonBlocking(PRBool *aNonBlocking);
|
|
NS_IMETHOD SetNonBlocking(PRBool aNonBlocking);
|
|
|
|
nsPipeInputStream() : mReaderRefCnt(0), mBlocking(PR_TRUE) {}
|
|
nsresult Fill();
|
|
protected:
|
|
nsrefcnt mReaderRefCnt; // separate refcnt so that we know when to close the consumer
|
|
PRBool mBlocking;
|
|
};
|
|
|
|
class nsPipeOutputStream : public nsIBufferOutputStream {
|
|
public:
|
|
NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
|
|
NS_IMETHOD_(nsrefcnt) AddRef(void);
|
|
NS_IMETHOD_(nsrefcnt) Release(void);
|
|
// nsIBaseStream methods:
|
|
NS_IMETHOD Close(void);
|
|
NS_IMETHOD Write(const char* fromBuf, PRUint32 bufLen, PRUint32 *writeCount);
|
|
NS_IMETHOD Flush(void);
|
|
// nsIBufferOutputStream methods:
|
|
NS_IMETHOD GetBuffer(nsIBuffer * *aBuffer) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
NS_IMETHOD WriteSegments(nsReadSegmentFun reader, void* closure, PRUint32 count,
|
|
PRUint32 *writeCount);
|
|
NS_IMETHOD WriteFrom(nsIInputStream* fromStream, PRUint32 count, PRUint32 *writeCount);
|
|
NS_IMETHOD GetNonBlocking(PRBool *aNonBlocking);
|
|
NS_IMETHOD SetNonBlocking(PRBool aNonBlocking);
|
|
|
|
nsPipeOutputStream() : mWriterRefCnt(0), mBlocking(PR_TRUE) {}
|
|
protected:
|
|
nsrefcnt mWriterRefCnt; // separate refcnt so that we know when to close the producer
|
|
PRBool mBlocking;
|
|
};
|
|
|
|
friend class nsPipeInputStream;
|
|
friend class nsPipeOutputStream;
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
// nsIPipe methods:
|
|
NS_IMETHOD Initialize(PRUint32 segmentSize, PRUint32 maxSize,
|
|
nsIPipeObserver *observer, nsIAllocator *segmentAllocator) {
|
|
nsresult rv;
|
|
rv = mBuffer.Init(segmentSize, maxSize, segmentAllocator);
|
|
if (NS_FAILED(rv)) return rv;
|
|
mObserver = observer;
|
|
NS_IF_ADDREF(mObserver);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD GetInputStream(nsIBufferInputStream * *aInputStream) {
|
|
*aInputStream = &mInput;
|
|
NS_IF_ADDREF(*aInputStream);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD GetOutputStream(nsIBufferOutputStream * *aOutputStream) {
|
|
*aOutputStream = &mOutput;
|
|
NS_IF_ADDREF(*aOutputStream);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD GetObserver(nsIPipeObserver* *result) {
|
|
*result = mObserver;
|
|
NS_IF_ADDREF(*result);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD SetObserver(nsIPipeObserver* obs) {
|
|
NS_IF_RELEASE(mObserver);
|
|
mObserver = obs;
|
|
NS_IF_ADDREF(mObserver);
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsPipe methods:
|
|
nsPipe();
|
|
virtual ~nsPipe();
|
|
|
|
nsPipeInputStream* GetInputStream() { return &mInput; }
|
|
nsPipeOutputStream* GetOutputStream() { return &mOutput; }
|
|
|
|
nsresult GetReadSegment(PRUint32 segmentLogicalOffset,
|
|
const char* *resultSegment,
|
|
PRUint32 *resultSegmentLen);
|
|
nsresult GetWriteSegment(char* *resultSegment,
|
|
PRUint32 *resultSegmentLen);
|
|
|
|
protected:
|
|
nsPipeInputStream mInput;
|
|
nsPipeOutputStream mOutput;
|
|
|
|
nsSegmentedBuffer mBuffer;
|
|
nsIPipeObserver* mObserver;
|
|
|
|
char* mReadCursor;
|
|
char* mReadLimit;
|
|
|
|
char* mWriteCursor;
|
|
char* mWriteLimit;
|
|
|
|
nsresult mCondition;
|
|
};
|
|
|
|
#define GET_INPUTSTREAM_PIPE(_this) \
|
|
((nsPipe*)((char*)(_this) - offsetof(nsPipe, mInput)))
|
|
|
|
#define GET_OUTPUTSTREAM_PIPE(_this) \
|
|
((nsPipe*)((char*)(_this) - offsetof(nsPipe, mOutput)))
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// nsPipe methods:
|
|
|
|
nsPipe::nsPipe()
|
|
: mObserver(nsnull),
|
|
mReadCursor(nsnull),
|
|
mReadLimit(nsnull),
|
|
mWriteCursor(nsnull),
|
|
mWriteLimit(nsnull),
|
|
mCondition(NS_OK)
|
|
{
|
|
NS_INIT_REFCNT();
|
|
}
|
|
|
|
nsPipe::~nsPipe()
|
|
{
|
|
NS_IF_RELEASE(mObserver);
|
|
}
|
|
|
|
NS_IMPL_THREADSAFE_ADDREF(nsPipe);
|
|
NS_IMPL_THREADSAFE_RELEASE(nsPipe);
|
|
|
|
NS_IMETHODIMP
|
|
nsPipe::QueryInterface(const nsIID& aIID, void** aInstancePtr)
|
|
{
|
|
if (aInstancePtr == nsnull)
|
|
return NS_ERROR_NULL_POINTER;
|
|
if (aIID.Equals(nsIBufferInputStream::GetIID()) ||
|
|
aIID.Equals(nsIInputStream::GetIID()) ||
|
|
aIID.Equals(nsIBaseStream::GetIID())) {
|
|
nsIBufferInputStream* in = GetInputStream();
|
|
NS_ADDREF(in);
|
|
*aInstancePtr = in;
|
|
return NS_OK;
|
|
}
|
|
if (aIID.Equals(nsIBufferOutputStream::GetIID()) ||
|
|
aIID.Equals(nsIOutputStream::GetIID()) ||
|
|
aIID.Equals(nsIBaseStream::GetIID())) {
|
|
nsIBufferOutputStream* out = GetOutputStream();
|
|
NS_ADDREF(out);
|
|
*aInstancePtr = out;
|
|
return NS_OK;
|
|
}
|
|
if (aIID.Equals(nsIPipe::GetIID()) ||
|
|
aIID.Equals(nsCOMTypeInfo<nsISupports>::GetIID())) {
|
|
*aInstancePtr = this;
|
|
NS_ADDREF_THIS();
|
|
return NS_OK;
|
|
}
|
|
return NS_NOINTERFACE;
|
|
}
|
|
|
|
nsresult
|
|
nsPipe::GetReadSegment(PRUint32 segmentLogicalOffset,
|
|
const char* *resultSegment,
|
|
PRUint32 *resultSegmentLen)
|
|
{
|
|
nsAutoCMonitor mon(this);
|
|
|
|
PRInt32 offset = (PRInt32)segmentLogicalOffset;
|
|
PRInt32 segCount = mBuffer.GetSegmentCount();
|
|
for (PRInt32 i = 0; i < segCount; i++) {
|
|
char* segStart = mBuffer.GetSegment(i);
|
|
char* segEnd = segStart + mBuffer.GetSegmentSize();
|
|
if (mReadCursor == nsnull) {
|
|
mReadCursor = segStart;
|
|
mReadLimit = segEnd;
|
|
}
|
|
else if (segStart <= mReadCursor && mReadCursor < segEnd) {
|
|
segStart = mReadCursor;
|
|
NS_ASSERTION(i == 0, "read cursor not in first segment");
|
|
}
|
|
if (segStart <= mWriteCursor && mWriteCursor < segEnd) {
|
|
segEnd = mWriteCursor;
|
|
NS_ASSERTION(i == segCount - 1, "write cursor not in last segment");
|
|
}
|
|
|
|
PRInt32 amt = segEnd - segStart;
|
|
if (offset < amt) {
|
|
// segmentLogicalOffset is in this segment, so read up to its end
|
|
*resultSegmentLen = amt - offset;
|
|
*resultSegment = segStart + offset;
|
|
return *resultSegmentLen == 0 ? mCondition : NS_OK;
|
|
}
|
|
offset -= amt;
|
|
}
|
|
*resultSegmentLen = 0;
|
|
*resultSegment = nsnull;
|
|
return *resultSegmentLen == 0 ? mCondition : NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsPipe::GetWriteSegment(char* *resultSegment,
|
|
PRUint32 *resultSegmentLen)
|
|
{
|
|
nsAutoCMonitor mon(this);
|
|
|
|
*resultSegment = nsnull;
|
|
*resultSegmentLen = 0;
|
|
if (mWriteCursor == nsnull ||
|
|
mWriteCursor == mWriteLimit) {
|
|
char* seg = mBuffer.AppendNewSegment();
|
|
if (seg == nsnull) {
|
|
// buffer is full
|
|
return NS_OK;
|
|
}
|
|
mWriteCursor = seg;
|
|
mWriteLimit = seg + mBuffer.GetSegmentSize();
|
|
}
|
|
|
|
*resultSegment = mWriteCursor;
|
|
*resultSegmentLen = mWriteLimit - mWriteCursor;
|
|
return NS_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// nsPipeInputStream methods:
|
|
|
|
NS_IMETHODIMP
|
|
nsPipe::nsPipeInputStream::QueryInterface(const nsIID& aIID, void** aInstancePtr)
|
|
{
|
|
return GET_INPUTSTREAM_PIPE(this)->QueryInterface(aIID, aInstancePtr);
|
|
}
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
nsPipe::nsPipeInputStream::AddRef(void)
|
|
{
|
|
mReaderRefCnt++;
|
|
return GET_INPUTSTREAM_PIPE(this)->AddRef();
|
|
}
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
nsPipe::nsPipeInputStream::Release(void)
|
|
{
|
|
if (--mReaderRefCnt == 0)
|
|
Close();
|
|
return GET_INPUTSTREAM_PIPE(this)->Release();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPipe::nsPipeInputStream::Close(void)
|
|
{
|
|
nsPipe* pipe = GET_INPUTSTREAM_PIPE(this);
|
|
nsAutoCMonitor mon(pipe);
|
|
|
|
pipe->mCondition = NS_BASE_STREAM_CLOSED;
|
|
pipe->mBuffer.Empty();
|
|
pipe->mWriteCursor = nsnull;
|
|
pipe->mWriteLimit = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPipe::nsPipeInputStream::Available(PRUint32 *result)
|
|
{
|
|
nsPipe* pipe = GET_INPUTSTREAM_PIPE(this);
|
|
nsAutoCMonitor mon(pipe);
|
|
|
|
PRUint32 len = pipe->mBuffer.GetSize();
|
|
if (pipe->mReadCursor)
|
|
len -= pipe->mBuffer.GetSegmentSize() - (pipe->mReadLimit - pipe->mReadCursor);
|
|
if (pipe->mWriteCursor)
|
|
len -= pipe->mWriteLimit - pipe->mWriteCursor;
|
|
|
|
*result = len;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPipe::nsPipeInputStream::ReadSegments(nsWriteSegmentFun writer,
|
|
void* closure,
|
|
PRUint32 count,
|
|
PRUint32 *readCount)
|
|
{
|
|
nsPipe* pipe = GET_INPUTSTREAM_PIPE(this);
|
|
nsAutoCMonitor mon(pipe);
|
|
|
|
nsresult rv = NS_OK;
|
|
PRUint32 readBufferLen;
|
|
const char* readBuffer;
|
|
|
|
*readCount = 0;
|
|
while (count > 0) {
|
|
rv = pipe->GetReadSegment(0, &readBuffer, &readBufferLen);
|
|
if (NS_FAILED(rv))
|
|
goto done;
|
|
if (readBufferLen == 0) {
|
|
rv = pipe->mCondition;
|
|
if (*readCount > 0 || NS_FAILED(rv))
|
|
goto done; // don't Fill if we've got something
|
|
if (pipe->mObserver) {
|
|
mon.Exit(); // XXXbe avoid deadlock better
|
|
rv = pipe->mObserver->OnEmpty(pipe);
|
|
mon.Enter();
|
|
if (NS_FAILED(rv)) goto done;
|
|
}
|
|
rv = Fill();
|
|
if (/*rv == NS_BASE_STREAM_WOULD_BLOCK || */NS_FAILED(rv))
|
|
goto done;
|
|
// else we filled the pipe, so go around again
|
|
continue;
|
|
}
|
|
|
|
readBufferLen = PR_MIN(readBufferLen, count);
|
|
while (readBufferLen > 0) {
|
|
PRUint32 writeCount;
|
|
rv = writer(closure, readBuffer, *readCount, readBufferLen, &writeCount);
|
|
if (writeCount == 0 || rv == NS_BASE_STREAM_WOULD_BLOCK) {
|
|
rv = pipe->mCondition;
|
|
if (*readCount > 0 || NS_FAILED(rv))
|
|
goto done; // don't Fill if we've got something
|
|
rv = Fill();
|
|
if (/*rv == NS_BASE_STREAM_WOULD_BLOCK || */NS_FAILED(rv))
|
|
goto done;
|
|
// else we filled the pipe, so go around again
|
|
continue;
|
|
}
|
|
if (NS_FAILED(rv))
|
|
goto done;
|
|
NS_ASSERTION(writeCount <= readBufferLen, "writer returned bad writeCount");
|
|
readBuffer += writeCount;
|
|
readBufferLen -= writeCount;
|
|
*readCount += writeCount;
|
|
count -= writeCount;
|
|
pipe->mReadCursor += writeCount;
|
|
}
|
|
if (pipe->mReadCursor == pipe->mReadLimit) {
|
|
pipe->mReadCursor = nsnull;
|
|
pipe->mReadLimit = nsnull;
|
|
PRBool empty = pipe->mBuffer.DeleteFirstSegment();
|
|
if (empty && pipe->mObserver) {
|
|
mon.Exit(); // XXXbe avoid deadlock better
|
|
rv = pipe->mObserver->OnEmpty(pipe);
|
|
mon.Enter();
|
|
if (NS_FAILED(rv))
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
done:
|
|
if (mBlocking && rv == NS_BASE_STREAM_WOULD_BLOCK && *readCount > 0) {
|
|
mon.Notify(); // wake up writer
|
|
}
|
|
if (rv == NS_BASE_STREAM_CLOSED) // EOF
|
|
rv = NS_OK;
|
|
return *readCount == 0 ? rv : NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsPipe::nsPipeInputStream::Fill()
|
|
{
|
|
nsPipe* pipe = GET_INPUTSTREAM_PIPE(this);
|
|
nsAutoCMonitor mon(pipe);
|
|
|
|
nsresult rv;
|
|
while (PR_TRUE) {
|
|
// check read buffer again while in the monitor
|
|
PRUint32 amt;
|
|
const char* buf;
|
|
rv = pipe->GetReadSegment(0, &buf, &amt);
|
|
if (NS_FAILED(rv) || amt > 0) return rv;
|
|
|
|
// else notify the writer and wait
|
|
rv = mon.Notify();
|
|
if (NS_FAILED(rv)) return rv; // interrupted
|
|
if (mBlocking) {
|
|
rv = mon.Wait();
|
|
if (NS_FAILED(rv)) return rv; // interrupted
|
|
}
|
|
else {
|
|
return NS_BASE_STREAM_WOULD_BLOCK;
|
|
}
|
|
// loop again so that we end up exiting on EOF with
|
|
// the right error
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
static NS_METHOD
|
|
nsWriteToRawBuffer(void* closure,
|
|
const char* fromRawSegment,
|
|
PRUint32 offset,
|
|
PRUint32 count,
|
|
PRUint32 *writeCount)
|
|
{
|
|
char* toBuf = (char*)closure;
|
|
nsCRT::memcpy(&toBuf[offset], fromRawSegment, count);
|
|
*writeCount = count;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPipe::nsPipeInputStream::Read(char* toBuf, PRUint32 bufLen, PRUint32 *readCount)
|
|
{
|
|
return ReadSegments(nsWriteToRawBuffer, toBuf, bufLen, readCount);
|
|
}
|
|
|
|
#define COMPARE(s1, s2, i) \
|
|
(ignoreCase \
|
|
? nsCRT::strncasecmp((const char *)s1, (const char *)s2, (PRUint32)i) \
|
|
: nsCRT::strncmp((const char *)s1, (const char *)s2, (PRUint32)i))
|
|
|
|
NS_IMETHODIMP
|
|
nsPipe::nsPipeInputStream::Search(const char *forString,
|
|
PRBool ignoreCase,
|
|
PRBool *found,
|
|
PRUint32 *offsetSearchedTo)
|
|
{
|
|
nsPipe* pipe = GET_INPUTSTREAM_PIPE(this);
|
|
|
|
nsresult rv;
|
|
const char* bufSeg1;
|
|
PRUint32 bufSegLen1;
|
|
PRUint32 segmentPos = 0;
|
|
PRUint32 strLen = nsCRT::strlen(forString);
|
|
|
|
rv = pipe->GetReadSegment(segmentPos, &bufSeg1, &bufSegLen1);
|
|
if (NS_FAILED(rv) || bufSegLen1 == 0) {
|
|
*found = PR_FALSE;
|
|
*offsetSearchedTo = segmentPos;
|
|
return NS_OK;
|
|
}
|
|
|
|
while (PR_TRUE) {
|
|
PRUint32 i;
|
|
// check if the string is in the buffer segment
|
|
for (i = 0; i < bufSegLen1 - strLen + 1; i++) {
|
|
if (COMPARE(&bufSeg1[i], forString, strLen) == 0) {
|
|
*found = PR_TRUE;
|
|
*offsetSearchedTo = segmentPos + i;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
// get the next segment
|
|
const char* bufSeg2;
|
|
PRUint32 bufSegLen2;
|
|
segmentPos += bufSegLen1;
|
|
rv = pipe->GetReadSegment(segmentPos, &bufSeg2, &bufSegLen2);
|
|
if (NS_FAILED(rv) || bufSegLen2 == 0) {
|
|
*found = PR_FALSE;
|
|
if (NS_FAILED(pipe->mCondition))
|
|
*offsetSearchedTo = segmentPos;
|
|
else
|
|
*offsetSearchedTo = segmentPos - strLen + 1;
|
|
return NS_OK;
|
|
}
|
|
|
|
// check if the string is straddling the next buffer segment
|
|
PRUint32 limit = PR_MIN(strLen, bufSegLen2 + 1);
|
|
for (i = 0; i < limit; i++) {
|
|
PRUint32 strPart1Len = strLen - i - 1;
|
|
PRUint32 strPart2Len = strLen - strPart1Len;
|
|
const char* strPart2 = &forString[strLen - strPart2Len];
|
|
PRUint32 bufSeg1Offset = bufSegLen1 - strPart1Len;
|
|
if (COMPARE(&bufSeg1[bufSeg1Offset], forString, strPart1Len) == 0 &&
|
|
COMPARE(bufSeg2, strPart2, strPart2Len) == 0) {
|
|
*found = PR_TRUE;
|
|
*offsetSearchedTo = segmentPos - strPart1Len;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
// finally continue with the next buffer
|
|
bufSeg1 = bufSeg2;
|
|
bufSegLen1 = bufSegLen2;
|
|
}
|
|
NS_NOTREACHED("can't get here");
|
|
return NS_ERROR_FAILURE; // keep compiler happy
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPipe::nsPipeInputStream::GetNonBlocking(PRBool *aNonBlocking)
|
|
{
|
|
*aNonBlocking = !mBlocking;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPipe::nsPipeInputStream::SetNonBlocking(PRBool aNonBlocking)
|
|
{
|
|
mBlocking = !aNonBlocking;
|
|
return NS_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// nsPipeOutputStream methods:
|
|
|
|
NS_IMETHODIMP
|
|
nsPipe::nsPipeOutputStream::QueryInterface(const nsIID& aIID, void** aInstancePtr)
|
|
{
|
|
return GET_OUTPUTSTREAM_PIPE(this)->QueryInterface(aIID, aInstancePtr);
|
|
}
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
nsPipe::nsPipeOutputStream::AddRef(void)
|
|
{
|
|
mWriterRefCnt++;
|
|
return GET_OUTPUTSTREAM_PIPE(this)->AddRef();
|
|
}
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
nsPipe::nsPipeOutputStream::Release(void)
|
|
{
|
|
if (--mWriterRefCnt == 0)
|
|
Close();
|
|
return GET_OUTPUTSTREAM_PIPE(this)->Release();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPipe::nsPipeOutputStream::Close(void)
|
|
{
|
|
nsPipe* pipe = GET_OUTPUTSTREAM_PIPE(this);
|
|
nsAutoCMonitor mon(pipe);
|
|
pipe->mCondition = NS_BASE_STREAM_CLOSED;
|
|
nsresult rv = mon.Notify(); // wake up the writer
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPipe::nsPipeOutputStream::WriteSegments(nsReadSegmentFun reader,
|
|
void* closure,
|
|
PRUint32 count,
|
|
PRUint32 *writeCount)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
nsPipe* pipe = GET_OUTPUTSTREAM_PIPE(this);
|
|
{
|
|
nsAutoCMonitor mon(pipe);
|
|
|
|
*writeCount = 0;
|
|
if (NS_FAILED(pipe->mCondition)
|
|
&& pipe->mCondition != NS_BASE_STREAM_CLOSED) {
|
|
rv = pipe->mCondition;
|
|
goto done;
|
|
}
|
|
|
|
while (count > 0) {
|
|
PRUint32 writeBufLen;
|
|
char* writeBuf;
|
|
rv = pipe->GetWriteSegment(&writeBuf, &writeBufLen);
|
|
if (NS_FAILED(rv))
|
|
goto done;
|
|
if (writeBufLen == 0) {
|
|
if (pipe->mObserver && *writeCount == 0) {
|
|
mon.Exit(); // XXXbe avoid deadlock better
|
|
rv = pipe->mObserver->OnFull(pipe);
|
|
mon.Enter();
|
|
if (NS_FAILED(rv)) goto done;
|
|
}
|
|
rv = Flush();
|
|
if (/*rv == NS_BASE_STREAM_WOULD_BLOCK || */NS_FAILED(rv))
|
|
goto done;
|
|
// else we flushed, so go around again
|
|
continue;
|
|
}
|
|
|
|
writeBufLen = PR_MIN(writeBufLen, count);
|
|
while (writeBufLen > 0) {
|
|
PRUint32 readCount = 0;
|
|
rv = reader(closure, writeBuf, *writeCount, writeBufLen, &readCount);
|
|
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
|
|
// The reader didn't have anything else to put in the buffer, so
|
|
// call flush to notify the guy downstream, hoping that he'll somehow
|
|
// wake up the guy upstream to eventually produce more data for us.
|
|
nsresult rv2 = Flush();
|
|
if (/*rv2 == NS_BASE_STREAM_WOULD_BLOCK || */NS_FAILED(rv2))
|
|
goto done;
|
|
// else we flushed, so go around again
|
|
continue;
|
|
}
|
|
if (NS_FAILED(rv)) {
|
|
// save the failure condition so that we can get it again later
|
|
pipe->mCondition = rv;
|
|
goto done;
|
|
}
|
|
if (readCount == 0) { // EOF
|
|
pipe->mCondition = NS_BASE_STREAM_CLOSED;
|
|
goto done;
|
|
}
|
|
NS_ASSERTION(readCount <= writeBufLen, "reader returned bad readCount");
|
|
writeBuf += readCount;
|
|
writeBufLen -= readCount;
|
|
*writeCount += readCount;
|
|
count -= readCount;
|
|
pipe->mWriteCursor += readCount;
|
|
}
|
|
if (pipe->mWriteCursor == pipe->mWriteLimit) {
|
|
pipe->mWriteCursor = nsnull;
|
|
pipe->mWriteLimit = nsnull;
|
|
}
|
|
}
|
|
done:
|
|
if (mBlocking && rv == NS_BASE_STREAM_WOULD_BLOCK && *writeCount > 0) {
|
|
mon.Notify(); // wake up reader
|
|
}
|
|
} // exit monitor
|
|
|
|
if (pipe->mObserver && *writeCount > 0) {
|
|
pipe->mObserver->OnWrite(pipe, *writeCount);
|
|
}
|
|
return *writeCount == 0 ? rv : NS_OK;
|
|
}
|
|
|
|
static NS_METHOD
|
|
nsReadFromRawBuffer(void* closure,
|
|
char* toRawSegment,
|
|
PRUint32 offset,
|
|
PRUint32 count,
|
|
PRUint32 *readCount)
|
|
{
|
|
const char* fromBuf = (const char*)closure;
|
|
nsCRT::memcpy(toRawSegment, &fromBuf[offset], count);
|
|
*readCount = count;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPipe::nsPipeOutputStream::Write(const char* fromBuf,
|
|
PRUint32 bufLen,
|
|
PRUint32 *writeCount)
|
|
{
|
|
return WriteSegments(nsReadFromRawBuffer, (void*)fromBuf, bufLen, writeCount);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPipe::nsPipeOutputStream::Flush(void)
|
|
{
|
|
nsPipe* pipe = GET_OUTPUTSTREAM_PIPE(this);
|
|
nsAutoCMonitor mon(pipe);
|
|
nsresult rv = NS_OK;
|
|
PRBool firstTime = PR_TRUE;
|
|
while (PR_TRUE) {
|
|
// check write buffer again while in the monitor
|
|
PRUint32 amt;
|
|
const char* buf;
|
|
rv = pipe->GetReadSegment(0, &buf, &amt);
|
|
if (firstTime && amt == 0) {
|
|
// If we think we needed to flush, yet there's nothing
|
|
// in the buffer to read, we must have not been able to
|
|
// allocate any segments.
|
|
return NS_BASE_STREAM_WOULD_BLOCK;
|
|
}
|
|
if (NS_FAILED(rv) || amt == 0) return rv;
|
|
firstTime = PR_FALSE;
|
|
|
|
// else notify the reader and wait
|
|
rv = mon.Notify();
|
|
if (NS_FAILED(rv)) return rv; // interrupted
|
|
if (mBlocking) {
|
|
rv = mon.Wait();
|
|
if (NS_FAILED(rv)) return rv; // interrupted
|
|
}
|
|
else {
|
|
return NS_BASE_STREAM_WOULD_BLOCK;
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
static NS_METHOD
|
|
nsReadFromInputStream(void* closure,
|
|
char* toRawSegment,
|
|
PRUint32 offset,
|
|
PRUint32 count,
|
|
PRUint32 *readCount)
|
|
{
|
|
nsIInputStream* fromStream = (nsIInputStream*)closure;
|
|
return fromStream->Read(toRawSegment, count, readCount);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPipe::nsPipeOutputStream::WriteFrom(nsIInputStream* fromStream,
|
|
PRUint32 count,
|
|
PRUint32 *writeCount)
|
|
{
|
|
return WriteSegments(nsReadFromInputStream, fromStream, count, writeCount);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPipe::nsPipeOutputStream::GetNonBlocking(PRBool *aNonBlocking)
|
|
{
|
|
*aNonBlocking = !mBlocking;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPipe::nsPipeOutputStream::SetNonBlocking(PRBool aNonBlocking)
|
|
{
|
|
mBlocking = !aNonBlocking;
|
|
return NS_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef PAGE_MANAGER
|
|
static NS_DEFINE_CID(kPageManagerCID, NS_PAGEMANAGER_CID);
|
|
#endif
|
|
static NS_DEFINE_CID(kAllocatorCID, NS_ALLOCATOR_CID);
|
|
|
|
NS_COM nsresult
|
|
NS_NewPipe(nsIBufferInputStream* *inStrResult,
|
|
nsIBufferOutputStream* *outStrResult,
|
|
nsIPipeObserver* observer,
|
|
PRUint32 segmentSize,
|
|
PRUint32 maxSize)
|
|
{
|
|
nsresult rv;
|
|
const nsCID* cid = &kAllocatorCID;
|
|
#ifdef PAGE_MANAGER
|
|
// Take the page manager out altogether because some unices don't
|
|
// know how to reserve VM -- only preallocate it which takes up a lot
|
|
// of space.
|
|
#ifndef XP_MAC
|
|
// Don't use page buffers on the mac because we don't really have
|
|
// VM there, and they end up being more wasteful:
|
|
if (segmentSize >= NS_PAGEMGR_PAGE_SIZE) {
|
|
cid = &kPageManagerCID;
|
|
}
|
|
#endif
|
|
#endif
|
|
NS_WITH_SERVICE(nsIAllocator, alloc, *cid, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsPipe* pipe = new nsPipe();
|
|
if (pipe == nsnull)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
rv = pipe->Initialize(segmentSize, maxSize, observer, alloc);
|
|
if (NS_FAILED(rv)) {
|
|
delete pipe;
|
|
return rv;
|
|
}
|
|
*inStrResult = pipe->GetInputStream();
|
|
*outStrResult = pipe->GetOutputStream();
|
|
NS_ADDREF(*inStrResult);
|
|
NS_ADDREF(*outStrResult);
|
|
return NS_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|