michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "ipc/IPCMessageUtils.h" michael@0: michael@0: #include "nsBufferedStreams.h" michael@0: #include "nsStreamUtils.h" michael@0: #include "nsNetCID.h" michael@0: #include "nsIClassInfoImpl.h" michael@0: #include "mozilla/ipc/InputStreamUtils.h" michael@0: #include michael@0: michael@0: #ifdef DEBUG_brendan michael@0: # define METERING michael@0: #endif michael@0: michael@0: #ifdef METERING michael@0: # include michael@0: # define METER(x) x michael@0: # define MAX_BIG_SEEKS 20 michael@0: michael@0: static struct { michael@0: uint32_t mSeeksWithinBuffer; michael@0: uint32_t mSeeksOutsideBuffer; michael@0: uint32_t mBufferReadUponSeek; michael@0: uint32_t mBufferUnreadUponSeek; michael@0: uint32_t mBytesReadFromBuffer; michael@0: uint32_t mBigSeekIndex; michael@0: struct { michael@0: int64_t mOldOffset; michael@0: int64_t mNewOffset; michael@0: } mBigSeek[MAX_BIG_SEEKS]; michael@0: } bufstats; michael@0: #else michael@0: # define METER(x) /* nothing */ michael@0: #endif michael@0: michael@0: using namespace mozilla::ipc; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsBufferedStream michael@0: michael@0: nsBufferedStream::nsBufferedStream() michael@0: : mBuffer(nullptr), michael@0: mBufferStartOffset(0), michael@0: mCursor(0), michael@0: mFillPoint(0), michael@0: mStream(nullptr), michael@0: mBufferDisabled(false), michael@0: mEOF(false), michael@0: mGetBufferCount(0) michael@0: { michael@0: } michael@0: michael@0: nsBufferedStream::~nsBufferedStream() michael@0: { michael@0: Close(); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsBufferedStream, nsISeekableStream) michael@0: michael@0: nsresult michael@0: nsBufferedStream::Init(nsISupports* stream, uint32_t bufferSize) michael@0: { michael@0: NS_ASSERTION(stream, "need to supply a stream"); michael@0: NS_ASSERTION(mStream == nullptr, "already inited"); michael@0: mStream = stream; michael@0: NS_IF_ADDREF(mStream); michael@0: mBufferSize = bufferSize; michael@0: mBufferStartOffset = 0; michael@0: mCursor = 0; michael@0: const mozilla::fallible_t fallible = mozilla::fallible_t(); michael@0: mBuffer = new (fallible) char[bufferSize]; michael@0: if (mBuffer == nullptr) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsBufferedStream::Close() michael@0: { michael@0: NS_IF_RELEASE(mStream); michael@0: if (mBuffer) { michael@0: delete[] mBuffer; michael@0: mBuffer = nullptr; michael@0: mBufferSize = 0; michael@0: mBufferStartOffset = 0; michael@0: mCursor = 0; michael@0: mFillPoint = 0; michael@0: } michael@0: #ifdef METERING michael@0: { michael@0: static FILE *tfp; michael@0: if (!tfp) { michael@0: tfp = fopen("/tmp/bufstats", "w"); michael@0: if (tfp) michael@0: setvbuf(tfp, nullptr, _IOLBF, 0); michael@0: } michael@0: if (tfp) { michael@0: fprintf(tfp, "seeks within buffer: %u\n", michael@0: bufstats.mSeeksWithinBuffer); michael@0: fprintf(tfp, "seeks outside buffer: %u\n", michael@0: bufstats.mSeeksOutsideBuffer); michael@0: fprintf(tfp, "buffer read on seek: %u\n", michael@0: bufstats.mBufferReadUponSeek); michael@0: fprintf(tfp, "buffer unread on seek: %u\n", michael@0: bufstats.mBufferUnreadUponSeek); michael@0: fprintf(tfp, "bytes read from buffer: %u\n", michael@0: bufstats.mBytesReadFromBuffer); michael@0: for (uint32_t i = 0; i < bufstats.mBigSeekIndex; i++) { michael@0: fprintf(tfp, "bigseek[%u] = {old: %u, new: %u}\n", michael@0: i, michael@0: bufstats.mBigSeek[i].mOldOffset, michael@0: bufstats.mBigSeek[i].mNewOffset); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedStream::Seek(int32_t whence, int64_t offset) michael@0: { michael@0: if (mStream == nullptr) michael@0: return NS_BASE_STREAM_CLOSED; michael@0: michael@0: // If the underlying stream isn't a random access store, then fail early. michael@0: // We could possibly succeed for the case where the seek position denotes michael@0: // something that happens to be read into the buffer, but that would make michael@0: // the failure data-dependent. michael@0: nsresult rv; michael@0: nsCOMPtr ras = do_QueryInterface(mStream, &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: int64_t absPos = 0; michael@0: switch (whence) { michael@0: case nsISeekableStream::NS_SEEK_SET: michael@0: absPos = offset; michael@0: break; michael@0: case nsISeekableStream::NS_SEEK_CUR: michael@0: absPos = mBufferStartOffset; michael@0: absPos += mCursor; michael@0: absPos += offset; michael@0: break; michael@0: case nsISeekableStream::NS_SEEK_END: michael@0: absPos = -1; michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("bogus seek whence parameter"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: // Let mCursor point into the existing buffer if the new position is michael@0: // between the current cursor and the mFillPoint "fencepost" -- the michael@0: // client may never get around to a Read or Write after this Seek. michael@0: // Read and Write worry about flushing and filling in that event. michael@0: // But if we're at EOF, make sure to pass the seek through to the michael@0: // underlying stream, because it may have auto-closed itself and michael@0: // needs to reopen. michael@0: uint32_t offsetInBuffer = uint32_t(absPos - mBufferStartOffset); michael@0: if (offsetInBuffer <= mFillPoint && !mEOF) { michael@0: METER(bufstats.mSeeksWithinBuffer++); michael@0: mCursor = offsetInBuffer; michael@0: return NS_OK; michael@0: } michael@0: michael@0: METER(bufstats.mSeeksOutsideBuffer++); michael@0: METER(bufstats.mBufferReadUponSeek += mCursor); michael@0: METER(bufstats.mBufferUnreadUponSeek += mFillPoint - mCursor); michael@0: rv = Flush(); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = ras->Seek(whence, offset); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: mEOF = false; michael@0: michael@0: // Recompute whether the offset we're seeking to is in our buffer. michael@0: // Note that we need to recompute because Flush() might have michael@0: // changed mBufferStartOffset. michael@0: offsetInBuffer = uint32_t(absPos - mBufferStartOffset); michael@0: if (offsetInBuffer <= mFillPoint) { michael@0: // It's safe to just set mCursor to offsetInBuffer. In particular, we michael@0: // want to avoid calling Fill() here since we already have the data that michael@0: // was seeked to and calling Fill() might auto-close our underlying michael@0: // stream in some cases. michael@0: mCursor = offsetInBuffer; michael@0: return NS_OK; michael@0: } michael@0: michael@0: METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS) michael@0: bufstats.mBigSeek[bufstats.mBigSeekIndex].mOldOffset = michael@0: mBufferStartOffset + int64_t(mCursor)); michael@0: const int64_t minus1 = -1; michael@0: if (absPos == minus1) { michael@0: // then we had the SEEK_END case, above michael@0: int64_t tellPos; michael@0: rv = ras->Tell(&tellPos); michael@0: mBufferStartOffset = tellPos; michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: else { michael@0: mBufferStartOffset = absPos; michael@0: } michael@0: METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS) michael@0: bufstats.mBigSeek[bufstats.mBigSeekIndex++].mNewOffset = michael@0: mBufferStartOffset); michael@0: michael@0: mFillPoint = mCursor = 0; michael@0: return Fill(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedStream::Tell(int64_t *result) michael@0: { michael@0: if (mStream == nullptr) michael@0: return NS_BASE_STREAM_CLOSED; michael@0: michael@0: int64_t result64 = mBufferStartOffset; michael@0: result64 += mCursor; michael@0: *result = result64; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedStream::SetEOF() michael@0: { michael@0: if (mStream == nullptr) michael@0: return NS_BASE_STREAM_CLOSED; michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr ras = do_QueryInterface(mStream, &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = ras->SetEOF(); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mEOF = true; michael@0: return rv; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsBufferedInputStream michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(nsBufferedInputStream, nsBufferedStream) michael@0: NS_IMPL_RELEASE_INHERITED(nsBufferedInputStream, nsBufferedStream) michael@0: michael@0: NS_IMPL_CLASSINFO(nsBufferedInputStream, nullptr, nsIClassInfo::THREADSAFE, michael@0: NS_BUFFEREDINPUTSTREAM_CID) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsBufferedInputStream) michael@0: NS_INTERFACE_MAP_ENTRY(nsIInputStream) michael@0: NS_INTERFACE_MAP_ENTRY(nsIBufferedInputStream) michael@0: NS_INTERFACE_MAP_ENTRY(nsIStreamBufferAccess) michael@0: NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream) michael@0: NS_IMPL_QUERY_CLASSINFO(nsBufferedInputStream) michael@0: NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream) michael@0: michael@0: NS_IMPL_CI_INTERFACE_GETTER(nsBufferedInputStream, michael@0: nsIInputStream, michael@0: nsIBufferedInputStream, michael@0: nsISeekableStream, michael@0: nsIStreamBufferAccess) michael@0: michael@0: nsresult michael@0: nsBufferedInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) michael@0: { michael@0: NS_ENSURE_NO_AGGREGATION(aOuter); michael@0: michael@0: nsBufferedInputStream* stream = new nsBufferedInputStream(); michael@0: if (stream == nullptr) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: NS_ADDREF(stream); michael@0: nsresult rv = stream->QueryInterface(aIID, aResult); michael@0: NS_RELEASE(stream); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedInputStream::Init(nsIInputStream* stream, uint32_t bufferSize) michael@0: { michael@0: return nsBufferedStream::Init(stream, bufferSize); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedInputStream::Close() michael@0: { michael@0: nsresult rv1 = NS_OK, rv2; michael@0: if (mStream) { michael@0: rv1 = Source()->Close(); michael@0: NS_RELEASE(mStream); michael@0: } michael@0: rv2 = nsBufferedStream::Close(); michael@0: if (NS_FAILED(rv1)) return rv1; michael@0: return rv2; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedInputStream::Available(uint64_t *result) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: *result = 0; michael@0: if (mStream) { michael@0: rv = Source()->Available(result); michael@0: } michael@0: *result += (mFillPoint - mCursor); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedInputStream::Read(char * buf, uint32_t count, uint32_t *result) michael@0: { michael@0: if (mBufferDisabled) { michael@0: if (!mStream) { michael@0: *result = 0; michael@0: return NS_OK; michael@0: } michael@0: nsresult rv = Source()->Read(buf, count, result); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mBufferStartOffset += *result; // so nsBufferedStream::Tell works michael@0: if (*result == 0) { michael@0: mEOF = true; michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: return ReadSegments(NS_CopySegmentToBuffer, buf, count, result); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure, michael@0: uint32_t count, uint32_t *result) michael@0: { michael@0: *result = 0; michael@0: michael@0: if (!mStream) michael@0: return NS_OK; michael@0: michael@0: nsresult rv = NS_OK; michael@0: while (count > 0) { michael@0: uint32_t amt = std::min(count, mFillPoint - mCursor); michael@0: if (amt > 0) { michael@0: uint32_t read = 0; michael@0: rv = writer(this, closure, mBuffer + mCursor, *result, amt, &read); michael@0: if (NS_FAILED(rv)) { michael@0: // errors returned from the writer end here! michael@0: rv = NS_OK; michael@0: break; michael@0: } michael@0: *result += read; michael@0: count -= read; michael@0: mCursor += read; michael@0: } michael@0: else { michael@0: rv = Fill(); michael@0: if (NS_FAILED(rv) || mFillPoint == mCursor) michael@0: break; michael@0: } michael@0: } michael@0: return (*result > 0) ? NS_OK : rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedInputStream::IsNonBlocking(bool *aNonBlocking) michael@0: { michael@0: if (mStream) michael@0: return Source()->IsNonBlocking(aNonBlocking); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedInputStream::Fill() michael@0: { michael@0: if (mBufferDisabled) michael@0: return NS_OK; michael@0: NS_ENSURE_TRUE(mStream, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: nsresult rv; michael@0: int32_t rem = int32_t(mFillPoint - mCursor); michael@0: if (rem > 0) { michael@0: // slide the remainder down to the start of the buffer michael@0: // |<------------->|<--rem-->|<--->| michael@0: // b c f s michael@0: memcpy(mBuffer, mBuffer + mCursor, rem); michael@0: } michael@0: mBufferStartOffset += mCursor; michael@0: mFillPoint = rem; michael@0: mCursor = 0; michael@0: michael@0: uint32_t amt; michael@0: rv = Source()->Read(mBuffer + mFillPoint, mBufferSize - mFillPoint, &amt); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (amt == 0) michael@0: mEOF = true; michael@0: michael@0: mFillPoint += amt; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(char*) michael@0: nsBufferedInputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask) michael@0: { michael@0: NS_ASSERTION(mGetBufferCount == 0, "nested GetBuffer!"); michael@0: if (mGetBufferCount != 0) michael@0: return nullptr; michael@0: michael@0: if (mBufferDisabled) michael@0: return nullptr; michael@0: michael@0: char* buf = mBuffer + mCursor; michael@0: uint32_t rem = mFillPoint - mCursor; michael@0: if (rem == 0) { michael@0: if (NS_FAILED(Fill())) michael@0: return nullptr; michael@0: buf = mBuffer + mCursor; michael@0: rem = mFillPoint - mCursor; michael@0: } michael@0: michael@0: uint32_t mod = (NS_PTR_TO_INT32(buf) & aAlignMask); michael@0: if (mod) { michael@0: uint32_t pad = aAlignMask + 1 - mod; michael@0: if (pad > rem) michael@0: return nullptr; michael@0: michael@0: memset(buf, 0, pad); michael@0: mCursor += pad; michael@0: buf += pad; michael@0: rem -= pad; michael@0: } michael@0: michael@0: if (aLength > rem) michael@0: return nullptr; michael@0: mGetBufferCount++; michael@0: return buf; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(void) michael@0: nsBufferedInputStream::PutBuffer(char* aBuffer, uint32_t aLength) michael@0: { michael@0: NS_ASSERTION(mGetBufferCount == 1, "stray PutBuffer!"); michael@0: if (--mGetBufferCount != 0) michael@0: return; michael@0: michael@0: NS_ASSERTION(mCursor + aLength <= mFillPoint, "PutBuffer botch"); michael@0: mCursor += aLength; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedInputStream::DisableBuffering() michael@0: { michael@0: NS_ASSERTION(!mBufferDisabled, "redundant call to DisableBuffering!"); michael@0: NS_ASSERTION(mGetBufferCount == 0, michael@0: "DisableBuffer call between GetBuffer and PutBuffer!"); michael@0: if (mGetBufferCount != 0) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: // Empty the buffer so nsBufferedStream::Tell works. michael@0: mBufferStartOffset += mCursor; michael@0: mFillPoint = mCursor = 0; michael@0: mBufferDisabled = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedInputStream::EnableBuffering() michael@0: { michael@0: NS_ASSERTION(mBufferDisabled, "gratuitous call to EnableBuffering!"); michael@0: mBufferDisabled = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedInputStream::GetUnbufferedStream(nsISupports* *aStream) michael@0: { michael@0: // Empty the buffer so subsequent i/o trumps any buffered data. michael@0: mBufferStartOffset += mCursor; michael@0: mFillPoint = mCursor = 0; michael@0: michael@0: *aStream = mStream; michael@0: NS_IF_ADDREF(*aStream); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsBufferedInputStream::Serialize(InputStreamParams& aParams, michael@0: FileDescriptorArray& aFileDescriptors) michael@0: { michael@0: BufferedInputStreamParams params; michael@0: michael@0: if (mStream) { michael@0: nsCOMPtr stream = do_QueryInterface(mStream); michael@0: MOZ_ASSERT(stream); michael@0: michael@0: InputStreamParams wrappedParams; michael@0: SerializeInputStream(stream, wrappedParams, aFileDescriptors); michael@0: michael@0: params.optionalStream() = wrappedParams; michael@0: } michael@0: else { michael@0: params.optionalStream() = mozilla::void_t(); michael@0: } michael@0: michael@0: params.bufferSize() = mBufferSize; michael@0: michael@0: aParams = params; michael@0: } michael@0: michael@0: bool michael@0: nsBufferedInputStream::Deserialize(const InputStreamParams& aParams, michael@0: const FileDescriptorArray& aFileDescriptors) michael@0: { michael@0: if (aParams.type() != InputStreamParams::TBufferedInputStreamParams) { michael@0: NS_ERROR("Received unknown parameters from the other process!"); michael@0: return false; michael@0: } michael@0: michael@0: const BufferedInputStreamParams& params = michael@0: aParams.get_BufferedInputStreamParams(); michael@0: const OptionalInputStreamParams& wrappedParams = params.optionalStream(); michael@0: michael@0: nsCOMPtr stream; michael@0: if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) { michael@0: stream = DeserializeInputStream(wrappedParams.get_InputStreamParams(), michael@0: aFileDescriptors); michael@0: if (!stream) { michael@0: NS_WARNING("Failed to deserialize wrapped stream!"); michael@0: return false; michael@0: } michael@0: } michael@0: else { michael@0: NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t, michael@0: "Unknown type for OptionalInputStreamParams!"); michael@0: } michael@0: michael@0: nsresult rv = Init(stream, params.bufferSize()); michael@0: NS_ENSURE_SUCCESS(rv, false); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsBufferedOutputStream michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(nsBufferedOutputStream, nsBufferedStream) michael@0: NS_IMPL_RELEASE_INHERITED(nsBufferedOutputStream, nsBufferedStream) michael@0: // This QI uses NS_INTERFACE_MAP_ENTRY_CONDITIONAL to check for michael@0: // non-nullness of mSafeStream. michael@0: NS_INTERFACE_MAP_BEGIN(nsBufferedOutputStream) michael@0: NS_INTERFACE_MAP_ENTRY(nsIOutputStream) michael@0: NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISafeOutputStream, mSafeStream) michael@0: NS_INTERFACE_MAP_ENTRY(nsIBufferedOutputStream) michael@0: NS_INTERFACE_MAP_ENTRY(nsIStreamBufferAccess) michael@0: NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream) michael@0: michael@0: nsresult michael@0: nsBufferedOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) michael@0: { michael@0: NS_ENSURE_NO_AGGREGATION(aOuter); michael@0: michael@0: nsBufferedOutputStream* stream = new nsBufferedOutputStream(); michael@0: if (stream == nullptr) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: NS_ADDREF(stream); michael@0: nsresult rv = stream->QueryInterface(aIID, aResult); michael@0: NS_RELEASE(stream); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedOutputStream::Init(nsIOutputStream* stream, uint32_t bufferSize) michael@0: { michael@0: // QI stream to an nsISafeOutputStream, to see if we should support it michael@0: mSafeStream = do_QueryInterface(stream); michael@0: michael@0: return nsBufferedStream::Init(stream, bufferSize); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedOutputStream::Close() michael@0: { michael@0: nsresult rv1, rv2 = NS_OK, rv3; michael@0: rv1 = Flush(); michael@0: // If we fail to Flush all the data, then we close anyway and drop the michael@0: // remaining data in the buffer. We do this because it's what Unix does michael@0: // for fclose and close. However, we report the error from Flush anyway. michael@0: if (mStream) { michael@0: rv2 = Sink()->Close(); michael@0: NS_RELEASE(mStream); michael@0: } michael@0: rv3 = nsBufferedStream::Close(); michael@0: if (NS_FAILED(rv1)) return rv1; michael@0: if (NS_FAILED(rv2)) return rv2; michael@0: return rv3; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedOutputStream::Write(const char *buf, uint32_t count, uint32_t *result) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: uint32_t written = 0; michael@0: while (count > 0) { michael@0: uint32_t amt = std::min(count, mBufferSize - mCursor); michael@0: if (amt > 0) { michael@0: memcpy(mBuffer + mCursor, buf + written, amt); michael@0: written += amt; michael@0: count -= amt; michael@0: mCursor += amt; michael@0: if (mFillPoint < mCursor) michael@0: mFillPoint = mCursor; michael@0: } michael@0: else { michael@0: NS_ASSERTION(mFillPoint, "iloop in nsBufferedOutputStream::Write!"); michael@0: rv = Flush(); michael@0: if (NS_FAILED(rv)) break; michael@0: } michael@0: } michael@0: *result = written; michael@0: return (written > 0) ? NS_OK : rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedOutputStream::Flush() michael@0: { michael@0: nsresult rv; michael@0: uint32_t amt; michael@0: if (!mStream) { michael@0: // Stream already cancelled/flushed; probably because of previous error. michael@0: return NS_OK; michael@0: } michael@0: rv = Sink()->Write(mBuffer, mFillPoint, &amt); michael@0: if (NS_FAILED(rv)) return rv; michael@0: mBufferStartOffset += amt; michael@0: if (amt == mFillPoint) { michael@0: mFillPoint = mCursor = 0; michael@0: return NS_OK; // flushed everything michael@0: } michael@0: michael@0: // slide the remainder down to the start of the buffer michael@0: // |<-------------->|<---|----->| michael@0: // b a c s michael@0: uint32_t rem = mFillPoint - amt; michael@0: memcpy(mBuffer, mBuffer + amt, rem); michael@0: mFillPoint = mCursor = rem; michael@0: return NS_ERROR_FAILURE; // didn't flush all michael@0: } michael@0: michael@0: // nsISafeOutputStream michael@0: NS_IMETHODIMP michael@0: nsBufferedOutputStream::Finish() michael@0: { michael@0: // flush the stream, to write out any buffered data... michael@0: nsresult rv = nsBufferedOutputStream::Flush(); michael@0: if (NS_FAILED(rv)) michael@0: NS_WARNING("failed to flush buffered data! possible dataloss"); michael@0: michael@0: // ... and finish the underlying stream... michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = mSafeStream->Finish(); michael@0: else michael@0: Sink()->Close(); michael@0: michael@0: // ... and close the buffered stream, so any further attempts to flush/close michael@0: // the buffered stream won't cause errors. michael@0: nsBufferedStream::Close(); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: static NS_METHOD michael@0: nsReadFromInputStream(nsIOutputStream* outStr, michael@0: void* closure, michael@0: char* toRawSegment, michael@0: uint32_t offset, michael@0: uint32_t count, michael@0: uint32_t *readCount) michael@0: { michael@0: nsIInputStream* fromStream = (nsIInputStream*)closure; michael@0: return fromStream->Read(toRawSegment, count, readCount); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedOutputStream::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval) michael@0: { michael@0: return WriteSegments(nsReadFromInputStream, inStr, count, _retval); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedOutputStream::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval) michael@0: { michael@0: *_retval = 0; michael@0: nsresult rv; michael@0: while (count > 0) { michael@0: uint32_t left = std::min(count, mBufferSize - mCursor); michael@0: if (left == 0) { michael@0: rv = Flush(); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: continue; michael@0: } michael@0: michael@0: uint32_t read = 0; michael@0: rv = reader(this, closure, mBuffer + mCursor, *_retval, left, &read); michael@0: michael@0: if (NS_FAILED(rv)) // If we have written some data, return ok michael@0: return (*_retval > 0) ? NS_OK : rv; michael@0: mCursor += read; michael@0: *_retval += read; michael@0: count -= read; michael@0: mFillPoint = std::max(mFillPoint, mCursor); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedOutputStream::IsNonBlocking(bool *aNonBlocking) michael@0: { michael@0: if (mStream) michael@0: return Sink()->IsNonBlocking(aNonBlocking); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(char*) michael@0: nsBufferedOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask) michael@0: { michael@0: NS_ASSERTION(mGetBufferCount == 0, "nested GetBuffer!"); michael@0: if (mGetBufferCount != 0) michael@0: return nullptr; michael@0: michael@0: if (mBufferDisabled) michael@0: return nullptr; michael@0: michael@0: char* buf = mBuffer + mCursor; michael@0: uint32_t rem = mBufferSize - mCursor; michael@0: if (rem == 0) { michael@0: if (NS_FAILED(Flush())) michael@0: return nullptr; michael@0: buf = mBuffer + mCursor; michael@0: rem = mBufferSize - mCursor; michael@0: } michael@0: michael@0: uint32_t mod = (NS_PTR_TO_INT32(buf) & aAlignMask); michael@0: if (mod) { michael@0: uint32_t pad = aAlignMask + 1 - mod; michael@0: if (pad > rem) michael@0: return nullptr; michael@0: michael@0: memset(buf, 0, pad); michael@0: mCursor += pad; michael@0: buf += pad; michael@0: rem -= pad; michael@0: } michael@0: michael@0: if (aLength > rem) michael@0: return nullptr; michael@0: mGetBufferCount++; michael@0: return buf; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(void) michael@0: nsBufferedOutputStream::PutBuffer(char* aBuffer, uint32_t aLength) michael@0: { michael@0: NS_ASSERTION(mGetBufferCount == 1, "stray PutBuffer!"); michael@0: if (--mGetBufferCount != 0) michael@0: return; michael@0: michael@0: NS_ASSERTION(mCursor + aLength <= mBufferSize, "PutBuffer botch"); michael@0: mCursor += aLength; michael@0: if (mFillPoint < mCursor) michael@0: mFillPoint = mCursor; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedOutputStream::DisableBuffering() michael@0: { michael@0: NS_ASSERTION(!mBufferDisabled, "redundant call to DisableBuffering!"); michael@0: NS_ASSERTION(mGetBufferCount == 0, michael@0: "DisableBuffer call between GetBuffer and PutBuffer!"); michael@0: if (mGetBufferCount != 0) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: // Empty the buffer so nsBufferedStream::Tell works. michael@0: nsresult rv = Flush(); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mBufferDisabled = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedOutputStream::EnableBuffering() michael@0: { michael@0: NS_ASSERTION(mBufferDisabled, "gratuitous call to EnableBuffering!"); michael@0: mBufferDisabled = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBufferedOutputStream::GetUnbufferedStream(nsISupports* *aStream) michael@0: { michael@0: // Empty the buffer so subsequent i/o trumps any buffered data. michael@0: if (mFillPoint) { michael@0: nsresult rv = Flush(); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: michael@0: *aStream = mStream; michael@0: NS_IF_ADDREF(*aStream); michael@0: return NS_OK; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////////