michael@0: /* vim:set ts=4 sw=4 sts=4 et cin: */ 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 "mozilla/Mutex.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "nsStreamUtils.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIPipe.h" michael@0: #include "nsIEventTarget.h" michael@0: #include "nsIRunnable.h" michael@0: #include "nsISafeOutputStream.h" michael@0: #include "nsString.h" michael@0: #include "nsIAsyncInputStream.h" michael@0: #include "nsIAsyncOutputStream.h" michael@0: #include "nsIBufferedStreams.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: class nsInputStreamReadyEvent MOZ_FINAL : public nsIRunnable michael@0: , public nsIInputStreamCallback michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: nsInputStreamReadyEvent(nsIInputStreamCallback *callback, michael@0: nsIEventTarget *target) michael@0: : mCallback(callback) michael@0: , mTarget(target) michael@0: { michael@0: } michael@0: michael@0: private: michael@0: ~nsInputStreamReadyEvent() michael@0: { michael@0: if (!mCallback) michael@0: return; michael@0: // michael@0: // whoa!! looks like we never posted this event. take care to michael@0: // release mCallback on the correct thread. if mTarget lives on the michael@0: // calling thread, then we are ok. otherwise, we have to try to michael@0: // proxy the Release over the right thread. if that thread is dead, michael@0: // then there's nothing we can do... better to leak than crash. michael@0: // michael@0: bool val; michael@0: nsresult rv = mTarget->IsOnCurrentThread(&val); michael@0: if (NS_FAILED(rv) || !val) { michael@0: nsCOMPtr event = michael@0: NS_NewInputStreamReadyEvent(mCallback, mTarget); michael@0: mCallback = nullptr; michael@0: if (event) { michael@0: rv = event->OnInputStreamReady(nullptr); michael@0: if (NS_FAILED(rv)) { michael@0: NS_NOTREACHED("leaking stream event"); michael@0: nsISupports *sup = event; michael@0: NS_ADDREF(sup); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: public: michael@0: NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *stream) michael@0: { michael@0: mStream = stream; michael@0: michael@0: nsresult rv = michael@0: mTarget->Dispatch(this, NS_DISPATCH_NORMAL); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Dispatch failed"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: if (mCallback) { michael@0: if (mStream) michael@0: mCallback->OnInputStreamReady(mStream); michael@0: mCallback = nullptr; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsCOMPtr mStream; michael@0: nsCOMPtr mCallback; michael@0: nsCOMPtr mTarget; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsInputStreamReadyEvent, nsIRunnable, michael@0: nsIInputStreamCallback) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: class nsOutputStreamReadyEvent MOZ_FINAL : public nsIRunnable michael@0: , public nsIOutputStreamCallback michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: nsOutputStreamReadyEvent(nsIOutputStreamCallback *callback, michael@0: nsIEventTarget *target) michael@0: : mCallback(callback) michael@0: , mTarget(target) michael@0: { michael@0: } michael@0: michael@0: private: michael@0: ~nsOutputStreamReadyEvent() michael@0: { michael@0: if (!mCallback) michael@0: return; michael@0: // michael@0: // whoa!! looks like we never posted this event. take care to michael@0: // release mCallback on the correct thread. if mTarget lives on the michael@0: // calling thread, then we are ok. otherwise, we have to try to michael@0: // proxy the Release over the right thread. if that thread is dead, michael@0: // then there's nothing we can do... better to leak than crash. michael@0: // michael@0: bool val; michael@0: nsresult rv = mTarget->IsOnCurrentThread(&val); michael@0: if (NS_FAILED(rv) || !val) { michael@0: nsCOMPtr event = michael@0: NS_NewOutputStreamReadyEvent(mCallback, mTarget); michael@0: mCallback = nullptr; michael@0: if (event) { michael@0: rv = event->OnOutputStreamReady(nullptr); michael@0: if (NS_FAILED(rv)) { michael@0: NS_NOTREACHED("leaking stream event"); michael@0: nsISupports *sup = event; michael@0: NS_ADDREF(sup); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: public: michael@0: NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *stream) michael@0: { michael@0: mStream = stream; michael@0: michael@0: nsresult rv = michael@0: mTarget->Dispatch(this, NS_DISPATCH_NORMAL); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("PostEvent failed"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: if (mCallback) { michael@0: if (mStream) michael@0: mCallback->OnOutputStreamReady(mStream); michael@0: mCallback = nullptr; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsCOMPtr mStream; michael@0: nsCOMPtr mCallback; michael@0: nsCOMPtr mTarget; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsOutputStreamReadyEvent, nsIRunnable, michael@0: nsIOutputStreamCallback) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: already_AddRefed michael@0: NS_NewInputStreamReadyEvent(nsIInputStreamCallback *callback, michael@0: nsIEventTarget *target) michael@0: { michael@0: NS_ASSERTION(callback, "null callback"); michael@0: NS_ASSERTION(target, "null target"); michael@0: nsRefPtr ev = michael@0: new nsInputStreamReadyEvent(callback, target); michael@0: return ev.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback *callback, michael@0: nsIEventTarget *target) michael@0: { michael@0: NS_ASSERTION(callback, "null callback"); michael@0: NS_ASSERTION(target, "null target"); michael@0: nsRefPtr ev = michael@0: new nsOutputStreamReadyEvent(callback, target); michael@0: return ev.forget(); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // NS_AsyncCopy implementation michael@0: michael@0: // abstract stream copier... michael@0: class nsAStreamCopier : public nsIInputStreamCallback michael@0: , public nsIOutputStreamCallback michael@0: , public nsIRunnable michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: nsAStreamCopier() michael@0: : mLock("nsAStreamCopier.mLock") michael@0: , mCallback(nullptr) michael@0: , mProgressCallback(nullptr) michael@0: , mClosure(nullptr) michael@0: , mChunkSize(0) michael@0: , mEventInProcess(false) michael@0: , mEventIsPending(false) michael@0: , mCloseSource(true) michael@0: , mCloseSink(true) michael@0: , mCanceled(false) michael@0: , mCancelStatus(NS_OK) michael@0: { michael@0: } michael@0: michael@0: // virtual since subclasses call superclass Release() michael@0: virtual ~nsAStreamCopier() michael@0: { michael@0: } michael@0: michael@0: // kick off the async copy... michael@0: nsresult Start(nsIInputStream *source, michael@0: nsIOutputStream *sink, michael@0: nsIEventTarget *target, michael@0: nsAsyncCopyCallbackFun callback, michael@0: void *closure, michael@0: uint32_t chunksize, michael@0: bool closeSource, michael@0: bool closeSink, michael@0: nsAsyncCopyProgressFun progressCallback) michael@0: { michael@0: mSource = source; michael@0: mSink = sink; michael@0: mTarget = target; michael@0: mCallback = callback; michael@0: mClosure = closure; michael@0: mChunkSize = chunksize; michael@0: mCloseSource = closeSource; michael@0: mCloseSink = closeSink; michael@0: mProgressCallback = progressCallback; michael@0: michael@0: mAsyncSource = do_QueryInterface(mSource); michael@0: mAsyncSink = do_QueryInterface(mSink); michael@0: michael@0: return PostContinuationEvent(); michael@0: } michael@0: michael@0: // implemented by subclasses, returns number of bytes copied and michael@0: // sets source and sink condition before returning. michael@0: virtual uint32_t DoCopy(nsresult *sourceCondition, nsresult *sinkCondition) = 0; michael@0: michael@0: void Process() michael@0: { michael@0: if (!mSource || !mSink) michael@0: return; michael@0: michael@0: nsresult sourceCondition, sinkCondition; michael@0: nsresult cancelStatus; michael@0: bool canceled; michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: canceled = mCanceled; michael@0: cancelStatus = mCancelStatus; michael@0: } michael@0: michael@0: // Copy data from the source to the sink until we hit failure or have michael@0: // copied all the data. michael@0: for (;;) { michael@0: // Note: copyFailed will be true if the source or the sink have michael@0: // reported an error, or if we failed to write any bytes michael@0: // because we have consumed all of our data. michael@0: bool copyFailed = false; michael@0: if (!canceled) { michael@0: uint32_t n = DoCopy(&sourceCondition, &sinkCondition); michael@0: if (n > 0 && mProgressCallback) { michael@0: mProgressCallback(mClosure, n); michael@0: } michael@0: copyFailed = NS_FAILED(sourceCondition) || michael@0: NS_FAILED(sinkCondition) || n == 0; michael@0: michael@0: MutexAutoLock lock(mLock); michael@0: canceled = mCanceled; michael@0: cancelStatus = mCancelStatus; michael@0: } michael@0: if (copyFailed && !canceled) { michael@0: if (sourceCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSource) { michael@0: // need to wait for more data from source. while waiting for michael@0: // more source data, be sure to observe failures on output end. michael@0: mAsyncSource->AsyncWait(this, 0, 0, nullptr); michael@0: michael@0: if (mAsyncSink) michael@0: mAsyncSink->AsyncWait(this, michael@0: nsIAsyncOutputStream::WAIT_CLOSURE_ONLY, michael@0: 0, nullptr); michael@0: break; michael@0: } michael@0: else if (sinkCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSink) { michael@0: // need to wait for more room in the sink. while waiting for michael@0: // more room in the sink, be sure to observer failures on the michael@0: // input end. michael@0: mAsyncSink->AsyncWait(this, 0, 0, nullptr); michael@0: michael@0: if (mAsyncSource) michael@0: mAsyncSource->AsyncWait(this, michael@0: nsIAsyncInputStream::WAIT_CLOSURE_ONLY, michael@0: 0, nullptr); michael@0: break; michael@0: } michael@0: } michael@0: if (copyFailed || canceled) { michael@0: if (mCloseSource) { michael@0: // close source michael@0: if (mAsyncSource) michael@0: mAsyncSource->CloseWithStatus(canceled ? cancelStatus : michael@0: sinkCondition); michael@0: else michael@0: mSource->Close(); michael@0: } michael@0: mAsyncSource = nullptr; michael@0: mSource = nullptr; michael@0: michael@0: if (mCloseSink) { michael@0: // close sink michael@0: if (mAsyncSink) michael@0: mAsyncSink->CloseWithStatus(canceled ? cancelStatus : michael@0: sourceCondition); michael@0: else { michael@0: // If we have an nsISafeOutputStream, and our michael@0: // sourceCondition and sinkCondition are not set to a michael@0: // failure state, finish writing. michael@0: nsCOMPtr sostream = michael@0: do_QueryInterface(mSink); michael@0: if (sostream && NS_SUCCEEDED(sourceCondition) && michael@0: NS_SUCCEEDED(sinkCondition)) michael@0: sostream->Finish(); michael@0: else michael@0: mSink->Close(); michael@0: } michael@0: } michael@0: mAsyncSink = nullptr; michael@0: mSink = nullptr; michael@0: michael@0: // notify state complete... michael@0: if (mCallback) { michael@0: nsresult status; michael@0: if (!canceled) { michael@0: status = sourceCondition; michael@0: if (NS_SUCCEEDED(status)) michael@0: status = sinkCondition; michael@0: if (status == NS_BASE_STREAM_CLOSED) michael@0: status = NS_OK; michael@0: } else { michael@0: status = cancelStatus; michael@0: } michael@0: mCallback(mClosure, status); michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsresult Cancel(nsresult aReason) michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: if (mCanceled) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (NS_SUCCEEDED(aReason)) { michael@0: NS_WARNING("cancel with non-failure status code"); michael@0: aReason = NS_BASE_STREAM_CLOSED; michael@0: } michael@0: michael@0: mCanceled = true; michael@0: mCancelStatus = aReason; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *source) michael@0: { michael@0: PostContinuationEvent(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *sink) michael@0: { michael@0: PostContinuationEvent(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // continuation event handler michael@0: NS_IMETHOD Run() michael@0: { michael@0: Process(); michael@0: michael@0: // clear "in process" flag and post any pending continuation event michael@0: MutexAutoLock lock(mLock); michael@0: mEventInProcess = false; michael@0: if (mEventIsPending) { michael@0: mEventIsPending = false; michael@0: PostContinuationEvent_Locked(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult PostContinuationEvent() michael@0: { michael@0: // we cannot post a continuation event if there is currently michael@0: // an event in process. doing so could result in Process being michael@0: // run simultaneously on multiple threads, so we mark the event michael@0: // as pending, and if an event is already in process then we michael@0: // just let that existing event take care of posting the real michael@0: // continuation event. michael@0: michael@0: MutexAutoLock lock(mLock); michael@0: return PostContinuationEvent_Locked(); michael@0: } michael@0: michael@0: nsresult PostContinuationEvent_Locked() michael@0: { michael@0: nsresult rv = NS_OK; michael@0: if (mEventInProcess) michael@0: mEventIsPending = true; michael@0: else { michael@0: rv = mTarget->Dispatch(this, NS_DISPATCH_NORMAL); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mEventInProcess = true; michael@0: else michael@0: NS_WARNING("unable to post continuation event"); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: protected: michael@0: nsCOMPtr mSource; michael@0: nsCOMPtr mSink; michael@0: nsCOMPtr mAsyncSource; michael@0: nsCOMPtr mAsyncSink; michael@0: nsCOMPtr mTarget; michael@0: Mutex mLock; michael@0: nsAsyncCopyCallbackFun mCallback; michael@0: nsAsyncCopyProgressFun mProgressCallback; michael@0: void *mClosure; michael@0: uint32_t mChunkSize; michael@0: bool mEventInProcess; michael@0: bool mEventIsPending; michael@0: bool mCloseSource; michael@0: bool mCloseSink; michael@0: bool mCanceled; michael@0: nsresult mCancelStatus; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsAStreamCopier, michael@0: nsIInputStreamCallback, michael@0: nsIOutputStreamCallback, michael@0: nsIRunnable) michael@0: michael@0: class nsStreamCopierIB MOZ_FINAL : public nsAStreamCopier michael@0: { michael@0: public: michael@0: nsStreamCopierIB() : nsAStreamCopier() {} michael@0: virtual ~nsStreamCopierIB() {} michael@0: michael@0: struct ReadSegmentsState { michael@0: nsIOutputStream *mSink; michael@0: nsresult mSinkCondition; michael@0: }; michael@0: michael@0: static NS_METHOD ConsumeInputBuffer(nsIInputStream *inStr, michael@0: void *closure, michael@0: const char *buffer, michael@0: uint32_t offset, michael@0: uint32_t count, michael@0: uint32_t *countWritten) michael@0: { michael@0: ReadSegmentsState *state = (ReadSegmentsState *) closure; michael@0: michael@0: nsresult rv = state->mSink->Write(buffer, count, countWritten); michael@0: if (NS_FAILED(rv)) michael@0: state->mSinkCondition = rv; michael@0: else if (*countWritten == 0) michael@0: state->mSinkCondition = NS_BASE_STREAM_CLOSED; michael@0: michael@0: return state->mSinkCondition; michael@0: } michael@0: michael@0: uint32_t DoCopy(nsresult *sourceCondition, nsresult *sinkCondition) michael@0: { michael@0: ReadSegmentsState state; michael@0: state.mSink = mSink; michael@0: state.mSinkCondition = NS_OK; michael@0: michael@0: uint32_t n; michael@0: *sourceCondition = michael@0: mSource->ReadSegments(ConsumeInputBuffer, &state, mChunkSize, &n); michael@0: *sinkCondition = state.mSinkCondition; michael@0: return n; michael@0: } michael@0: }; michael@0: michael@0: class nsStreamCopierOB MOZ_FINAL : public nsAStreamCopier michael@0: { michael@0: public: michael@0: nsStreamCopierOB() : nsAStreamCopier() {} michael@0: virtual ~nsStreamCopierOB() {} michael@0: michael@0: struct WriteSegmentsState { michael@0: nsIInputStream *mSource; michael@0: nsresult mSourceCondition; michael@0: }; michael@0: michael@0: static NS_METHOD FillOutputBuffer(nsIOutputStream *outStr, michael@0: void *closure, michael@0: char *buffer, michael@0: uint32_t offset, michael@0: uint32_t count, michael@0: uint32_t *countRead) michael@0: { michael@0: WriteSegmentsState *state = (WriteSegmentsState *) closure; michael@0: michael@0: nsresult rv = state->mSource->Read(buffer, count, countRead); michael@0: if (NS_FAILED(rv)) michael@0: state->mSourceCondition = rv; michael@0: else if (*countRead == 0) michael@0: state->mSourceCondition = NS_BASE_STREAM_CLOSED; michael@0: michael@0: return state->mSourceCondition; michael@0: } michael@0: michael@0: uint32_t DoCopy(nsresult *sourceCondition, nsresult *sinkCondition) michael@0: { michael@0: WriteSegmentsState state; michael@0: state.mSource = mSource; michael@0: state.mSourceCondition = NS_OK; michael@0: michael@0: uint32_t n; michael@0: *sinkCondition = michael@0: mSink->WriteSegments(FillOutputBuffer, &state, mChunkSize, &n); michael@0: *sourceCondition = state.mSourceCondition; michael@0: return n; michael@0: } michael@0: }; michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsresult michael@0: NS_AsyncCopy(nsIInputStream *source, michael@0: nsIOutputStream *sink, michael@0: nsIEventTarget *target, michael@0: nsAsyncCopyMode mode, michael@0: uint32_t chunkSize, michael@0: nsAsyncCopyCallbackFun callback, michael@0: void *closure, michael@0: bool closeSource, michael@0: bool closeSink, michael@0: nsISupports **aCopierCtx, michael@0: nsAsyncCopyProgressFun progressCallback) michael@0: { michael@0: NS_ASSERTION(target, "non-null target required"); michael@0: michael@0: nsresult rv; michael@0: nsAStreamCopier *copier; michael@0: michael@0: if (mode == NS_ASYNCCOPY_VIA_READSEGMENTS) michael@0: copier = new nsStreamCopierIB(); michael@0: else michael@0: copier = new nsStreamCopierOB(); michael@0: michael@0: if (!copier) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: // Start() takes an owning ref to the copier... michael@0: NS_ADDREF(copier); michael@0: rv = copier->Start(source, sink, target, callback, closure, chunkSize, michael@0: closeSource, closeSink, progressCallback); michael@0: michael@0: if (aCopierCtx) { michael@0: *aCopierCtx = static_cast( michael@0: static_cast(copier)); michael@0: NS_ADDREF(*aCopierCtx); michael@0: } michael@0: NS_RELEASE(copier); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsresult michael@0: NS_CancelAsyncCopy(nsISupports *aCopierCtx, nsresult aReason) michael@0: { michael@0: nsAStreamCopier *copier = static_cast( michael@0: static_cast(aCopierCtx)); michael@0: return copier->Cancel(aReason); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsresult michael@0: NS_ConsumeStream(nsIInputStream *stream, uint32_t maxCount, nsACString &result) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: result.Truncate(); michael@0: michael@0: while (maxCount) { michael@0: uint64_t avail64; michael@0: rv = stream->Available(&avail64); michael@0: if (NS_FAILED(rv)) { michael@0: if (rv == NS_BASE_STREAM_CLOSED) michael@0: rv = NS_OK; michael@0: break; michael@0: } michael@0: if (avail64 == 0) michael@0: break; michael@0: michael@0: uint32_t avail = (uint32_t)XPCOM_MIN(avail64, maxCount); michael@0: michael@0: // resize result buffer michael@0: uint32_t length = result.Length(); michael@0: if (avail > UINT32_MAX - length) michael@0: return NS_ERROR_FILE_TOO_BIG; michael@0: michael@0: result.SetLength(length + avail); michael@0: if (result.Length() != (length + avail)) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: char *buf = result.BeginWriting() + length; michael@0: michael@0: uint32_t n; michael@0: rv = stream->Read(buf, avail, &n); michael@0: if (NS_FAILED(rv)) michael@0: break; michael@0: if (n != avail) michael@0: result.SetLength(length + n); michael@0: if (n == 0) michael@0: break; michael@0: maxCount -= n; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: static NS_METHOD michael@0: TestInputStream(nsIInputStream *inStr, michael@0: void *closure, michael@0: const char *buffer, michael@0: uint32_t offset, michael@0: uint32_t count, michael@0: uint32_t *countWritten) michael@0: { michael@0: bool *result = static_cast(closure); michael@0: *result = true; michael@0: return NS_ERROR_ABORT; // don't call me anymore michael@0: } michael@0: michael@0: bool michael@0: NS_InputStreamIsBuffered(nsIInputStream *stream) michael@0: { michael@0: nsCOMPtr bufferedIn = do_QueryInterface(stream); michael@0: if (bufferedIn) { michael@0: return true; michael@0: } michael@0: michael@0: bool result = false; michael@0: uint32_t n; michael@0: nsresult rv = stream->ReadSegments(TestInputStream, michael@0: &result, 1, &n); michael@0: return result || NS_SUCCEEDED(rv); michael@0: } michael@0: michael@0: static NS_METHOD michael@0: TestOutputStream(nsIOutputStream *outStr, michael@0: void *closure, michael@0: char *buffer, michael@0: uint32_t offset, michael@0: uint32_t count, michael@0: uint32_t *countRead) michael@0: { michael@0: bool *result = static_cast(closure); michael@0: *result = true; michael@0: return NS_ERROR_ABORT; // don't call me anymore michael@0: } michael@0: michael@0: bool michael@0: NS_OutputStreamIsBuffered(nsIOutputStream *stream) michael@0: { michael@0: nsCOMPtr bufferedOut = do_QueryInterface(stream); michael@0: if (bufferedOut) { michael@0: return true; michael@0: } michael@0: michael@0: bool result = false; michael@0: uint32_t n; michael@0: stream->WriteSegments(TestOutputStream, &result, 1, &n); michael@0: return result; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_METHOD michael@0: NS_CopySegmentToStream(nsIInputStream *inStr, michael@0: void *closure, michael@0: const char *buffer, michael@0: uint32_t offset, michael@0: uint32_t count, michael@0: uint32_t *countWritten) michael@0: { michael@0: nsIOutputStream *outStr = static_cast(closure); michael@0: *countWritten = 0; michael@0: while (count) { michael@0: uint32_t n; michael@0: nsresult rv = outStr->Write(buffer, count, &n); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: buffer += n; michael@0: count -= n; michael@0: *countWritten += n; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_METHOD michael@0: NS_CopySegmentToBuffer(nsIInputStream *inStr, michael@0: void *closure, michael@0: const char *buffer, michael@0: uint32_t offset, michael@0: uint32_t count, michael@0: uint32_t *countWritten) michael@0: { michael@0: char *toBuf = static_cast(closure); michael@0: memcpy(&toBuf[offset], buffer, count); michael@0: *countWritten = count; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_METHOD michael@0: NS_CopySegmentToBuffer(nsIOutputStream *outStr, michael@0: void *closure, michael@0: char *buffer, michael@0: uint32_t offset, michael@0: uint32_t count, michael@0: uint32_t *countRead) michael@0: { michael@0: const char* fromBuf = static_cast(closure); michael@0: memcpy(buffer, &fromBuf[offset], count); michael@0: *countRead = count; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_METHOD michael@0: NS_DiscardSegment(nsIInputStream *inStr, michael@0: void *closure, michael@0: const char *buffer, michael@0: uint32_t offset, michael@0: uint32_t count, michael@0: uint32_t *countWritten) michael@0: { michael@0: *countWritten = count; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_METHOD michael@0: NS_WriteSegmentThunk(nsIInputStream *inStr, michael@0: void *closure, michael@0: const char *buffer, michael@0: uint32_t offset, michael@0: uint32_t count, michael@0: uint32_t *countWritten) michael@0: { michael@0: nsWriteSegmentThunk *thunk = static_cast(closure); michael@0: return thunk->mFun(thunk->mStream, thunk->mClosure, buffer, offset, count, michael@0: countWritten); michael@0: } michael@0: michael@0: NS_METHOD michael@0: NS_FillArray(FallibleTArray& aDest, nsIInputStream *aInput, michael@0: uint32_t aKeep, uint32_t *aNewBytes) michael@0: { michael@0: MOZ_ASSERT(aInput, "null stream"); michael@0: MOZ_ASSERT(aKeep <= aDest.Length(), "illegal keep count"); michael@0: michael@0: char* aBuffer = aDest.Elements(); michael@0: int64_t keepOffset = int64_t(aDest.Length()) - aKeep; michael@0: if (0 != aKeep && keepOffset > 0) { michael@0: memmove(aBuffer, aBuffer + keepOffset, aKeep); michael@0: } michael@0: michael@0: nsresult rv = michael@0: aInput->Read(aBuffer + aKeep, aDest.Capacity() - aKeep, aNewBytes); michael@0: if (NS_FAILED(rv)) { michael@0: *aNewBytes = 0; michael@0: } michael@0: // NOTE: we rely on the fact that the new slots are NOT initialized by michael@0: // SetLengthAndRetainStorage here, see nsTArrayElementTraits::Construct() michael@0: // in nsTArray.h: michael@0: aDest.SetLengthAndRetainStorage(aKeep + *aNewBytes); michael@0: michael@0: MOZ_ASSERT(aDest.Length() <= aDest.Capacity(), "buffer overflow"); michael@0: return rv; michael@0: }