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