xpcom/io/nsStreamUtils.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* vim:set ts=4 sw=4 sts=4 et cin: */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "mozilla/Mutex.h"
     7 #include "mozilla/Attributes.h"
     8 #include "nsStreamUtils.h"
     9 #include "nsAutoPtr.h"
    10 #include "nsCOMPtr.h"
    11 #include "nsIPipe.h"
    12 #include "nsIEventTarget.h"
    13 #include "nsIRunnable.h"
    14 #include "nsISafeOutputStream.h"
    15 #include "nsString.h"
    16 #include "nsIAsyncInputStream.h"
    17 #include "nsIAsyncOutputStream.h"
    18 #include "nsIBufferedStreams.h"
    20 using namespace mozilla;
    22 //-----------------------------------------------------------------------------
    24 class nsInputStreamReadyEvent MOZ_FINAL : public nsIRunnable
    25                                         , public nsIInputStreamCallback
    26 {
    27 public:
    28     NS_DECL_THREADSAFE_ISUPPORTS
    30     nsInputStreamReadyEvent(nsIInputStreamCallback *callback,
    31                             nsIEventTarget *target)
    32         : mCallback(callback)
    33         , mTarget(target)
    34     {
    35     }
    37 private:
    38     ~nsInputStreamReadyEvent()
    39     {
    40         if (!mCallback)
    41             return;
    42         //
    43         // whoa!!  looks like we never posted this event.  take care to
    44         // release mCallback on the correct thread.  if mTarget lives on the
    45         // calling thread, then we are ok.  otherwise, we have to try to 
    46         // proxy the Release over the right thread.  if that thread is dead,
    47         // then there's nothing we can do... better to leak than crash.
    48         //
    49         bool val;
    50         nsresult rv = mTarget->IsOnCurrentThread(&val);
    51         if (NS_FAILED(rv) || !val) {
    52             nsCOMPtr<nsIInputStreamCallback> event =
    53                 NS_NewInputStreamReadyEvent(mCallback, mTarget);
    54             mCallback = nullptr;
    55             if (event) {
    56                 rv = event->OnInputStreamReady(nullptr);
    57                 if (NS_FAILED(rv)) {
    58                     NS_NOTREACHED("leaking stream event");
    59                     nsISupports *sup = event;
    60                     NS_ADDREF(sup);
    61                 }
    62             }
    63         }
    64     }
    66 public:
    67     NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *stream)
    68     {
    69         mStream = stream;
    71         nsresult rv =
    72             mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
    73         if (NS_FAILED(rv)) {
    74             NS_WARNING("Dispatch failed");
    75             return NS_ERROR_FAILURE;
    76         }
    78         return NS_OK;
    79     }
    81     NS_IMETHOD Run()
    82     {
    83         if (mCallback) {
    84             if (mStream)
    85                 mCallback->OnInputStreamReady(mStream);
    86             mCallback = nullptr;
    87         }
    88         return NS_OK;
    89     }
    91 private:
    92     nsCOMPtr<nsIAsyncInputStream>    mStream;
    93     nsCOMPtr<nsIInputStreamCallback> mCallback;
    94     nsCOMPtr<nsIEventTarget>         mTarget;
    95 };
    97 NS_IMPL_ISUPPORTS(nsInputStreamReadyEvent, nsIRunnable,
    98                   nsIInputStreamCallback)
   100 //-----------------------------------------------------------------------------
   102 class nsOutputStreamReadyEvent MOZ_FINAL : public nsIRunnable
   103                                          , public nsIOutputStreamCallback
   104 {
   105 public:
   106     NS_DECL_THREADSAFE_ISUPPORTS
   108     nsOutputStreamReadyEvent(nsIOutputStreamCallback *callback,
   109                              nsIEventTarget *target)
   110         : mCallback(callback)
   111         , mTarget(target)
   112     {
   113     }
   115 private:
   116     ~nsOutputStreamReadyEvent()
   117     {
   118         if (!mCallback)
   119             return;
   120         //
   121         // whoa!!  looks like we never posted this event.  take care to
   122         // release mCallback on the correct thread.  if mTarget lives on the
   123         // calling thread, then we are ok.  otherwise, we have to try to 
   124         // proxy the Release over the right thread.  if that thread is dead,
   125         // then there's nothing we can do... better to leak than crash.
   126         //
   127         bool val;
   128         nsresult rv = mTarget->IsOnCurrentThread(&val);
   129         if (NS_FAILED(rv) || !val) {
   130             nsCOMPtr<nsIOutputStreamCallback> event =
   131                 NS_NewOutputStreamReadyEvent(mCallback, mTarget);
   132             mCallback = nullptr;
   133             if (event) {
   134                 rv = event->OnOutputStreamReady(nullptr);
   135                 if (NS_FAILED(rv)) {
   136                     NS_NOTREACHED("leaking stream event");
   137                     nsISupports *sup = event;
   138                     NS_ADDREF(sup);
   139                 }
   140             }
   141         }
   142     }
   144 public:
   145     NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *stream)
   146     {
   147         mStream = stream;
   149         nsresult rv =
   150             mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
   151         if (NS_FAILED(rv)) {
   152             NS_WARNING("PostEvent failed");
   153             return NS_ERROR_FAILURE;
   154         }
   156         return NS_OK;
   157     }
   159     NS_IMETHOD Run()
   160     {
   161         if (mCallback) {
   162             if (mStream)
   163                 mCallback->OnOutputStreamReady(mStream);
   164             mCallback = nullptr;
   165         }
   166         return NS_OK;
   167     }
   169 private:
   170     nsCOMPtr<nsIAsyncOutputStream>    mStream;
   171     nsCOMPtr<nsIOutputStreamCallback> mCallback;
   172     nsCOMPtr<nsIEventTarget>          mTarget;
   173 };
   175 NS_IMPL_ISUPPORTS(nsOutputStreamReadyEvent, nsIRunnable,
   176                   nsIOutputStreamCallback)
   178 //-----------------------------------------------------------------------------
   180 already_AddRefed<nsIInputStreamCallback>
   181 NS_NewInputStreamReadyEvent(nsIInputStreamCallback *callback,
   182                             nsIEventTarget *target)
   183 {
   184     NS_ASSERTION(callback, "null callback");
   185     NS_ASSERTION(target, "null target");
   186     nsRefPtr<nsInputStreamReadyEvent> ev =
   187         new nsInputStreamReadyEvent(callback, target);
   188     return ev.forget();
   189 }
   191 already_AddRefed<nsIOutputStreamCallback>
   192 NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback *callback,
   193                              nsIEventTarget *target)
   194 {
   195     NS_ASSERTION(callback, "null callback");
   196     NS_ASSERTION(target, "null target");
   197     nsRefPtr<nsOutputStreamReadyEvent> ev =
   198         new nsOutputStreamReadyEvent(callback, target);
   199     return ev.forget();
   200 }
   202 //-----------------------------------------------------------------------------
   203 // NS_AsyncCopy implementation
   205 // abstract stream copier...
   206 class nsAStreamCopier : public nsIInputStreamCallback
   207                       , public nsIOutputStreamCallback
   208                       , public nsIRunnable
   209 {
   210 public:
   211     NS_DECL_THREADSAFE_ISUPPORTS
   213     nsAStreamCopier()
   214         : mLock("nsAStreamCopier.mLock")
   215         , mCallback(nullptr)
   216         , mProgressCallback(nullptr)
   217         , mClosure(nullptr)
   218         , mChunkSize(0)
   219         , mEventInProcess(false)
   220         , mEventIsPending(false)
   221         , mCloseSource(true)
   222         , mCloseSink(true)
   223         , mCanceled(false)
   224         , mCancelStatus(NS_OK)
   225     {
   226     }
   228     // virtual since subclasses call superclass Release()
   229     virtual ~nsAStreamCopier()
   230     {
   231     }
   233     // kick off the async copy...
   234     nsresult Start(nsIInputStream *source,
   235                    nsIOutputStream *sink,
   236                    nsIEventTarget *target,
   237                    nsAsyncCopyCallbackFun callback,
   238                    void *closure,
   239                    uint32_t chunksize,
   240                    bool closeSource,
   241                    bool closeSink,
   242                    nsAsyncCopyProgressFun progressCallback)
   243     {
   244         mSource = source;
   245         mSink = sink;
   246         mTarget = target;
   247         mCallback = callback;
   248         mClosure = closure;
   249         mChunkSize = chunksize;
   250         mCloseSource = closeSource;
   251         mCloseSink = closeSink;
   252         mProgressCallback = progressCallback;
   254         mAsyncSource = do_QueryInterface(mSource);
   255         mAsyncSink = do_QueryInterface(mSink);
   257         return PostContinuationEvent();
   258     }
   260     // implemented by subclasses, returns number of bytes copied and
   261     // sets source and sink condition before returning.
   262     virtual uint32_t DoCopy(nsresult *sourceCondition, nsresult *sinkCondition) = 0;
   264     void Process()
   265     {
   266         if (!mSource || !mSink)
   267             return;
   269         nsresult sourceCondition, sinkCondition;
   270         nsresult cancelStatus;
   271         bool canceled;
   272         {
   273             MutexAutoLock lock(mLock);
   274             canceled = mCanceled;
   275             cancelStatus = mCancelStatus;
   276         }
   278         // Copy data from the source to the sink until we hit failure or have
   279         // copied all the data.
   280         for (;;) {
   281             // Note: copyFailed will be true if the source or the sink have
   282             //       reported an error, or if we failed to write any bytes
   283             //       because we have consumed all of our data.
   284             bool copyFailed = false;
   285             if (!canceled) {
   286                 uint32_t n = DoCopy(&sourceCondition, &sinkCondition);
   287                 if (n > 0 && mProgressCallback) {
   288                     mProgressCallback(mClosure, n);
   289                 }
   290                 copyFailed = NS_FAILED(sourceCondition) ||
   291                              NS_FAILED(sinkCondition) || n == 0;
   293                 MutexAutoLock lock(mLock);
   294                 canceled = mCanceled;
   295                 cancelStatus = mCancelStatus;
   296             }
   297             if (copyFailed && !canceled) {
   298                 if (sourceCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSource) {
   299                     // need to wait for more data from source.  while waiting for
   300                     // more source data, be sure to observe failures on output end.
   301                     mAsyncSource->AsyncWait(this, 0, 0, nullptr);
   303                     if (mAsyncSink)
   304                         mAsyncSink->AsyncWait(this,
   305                                               nsIAsyncOutputStream::WAIT_CLOSURE_ONLY,
   306                                               0, nullptr);
   307                     break;
   308                 }
   309                 else if (sinkCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSink) {
   310                     // need to wait for more room in the sink.  while waiting for
   311                     // more room in the sink, be sure to observer failures on the
   312                     // input end.
   313                     mAsyncSink->AsyncWait(this, 0, 0, nullptr);
   315                     if (mAsyncSource)
   316                         mAsyncSource->AsyncWait(this,
   317                                                 nsIAsyncInputStream::WAIT_CLOSURE_ONLY,
   318                                                 0, nullptr);
   319                     break;
   320                 }
   321             }
   322             if (copyFailed || canceled) {
   323                 if (mCloseSource) {
   324                     // close source
   325                     if (mAsyncSource)
   326                         mAsyncSource->CloseWithStatus(canceled ? cancelStatus :
   327                                                                  sinkCondition);
   328                     else
   329                         mSource->Close();
   330                 }
   331                 mAsyncSource = nullptr;
   332                 mSource = nullptr;
   334                 if (mCloseSink) {
   335                     // close sink
   336                     if (mAsyncSink)
   337                         mAsyncSink->CloseWithStatus(canceled ? cancelStatus :
   338                                                                sourceCondition);
   339                     else {
   340                         // If we have an nsISafeOutputStream, and our
   341                         // sourceCondition and sinkCondition are not set to a
   342                         // failure state, finish writing.
   343                         nsCOMPtr<nsISafeOutputStream> sostream =
   344                             do_QueryInterface(mSink);
   345                         if (sostream && NS_SUCCEEDED(sourceCondition) &&
   346                             NS_SUCCEEDED(sinkCondition))
   347                             sostream->Finish();
   348                         else
   349                             mSink->Close();
   350                     }
   351                 }
   352                 mAsyncSink = nullptr;
   353                 mSink = nullptr;
   355                 // notify state complete...
   356                 if (mCallback) {
   357                     nsresult status;
   358                     if (!canceled) {
   359                         status = sourceCondition;
   360                         if (NS_SUCCEEDED(status))
   361                             status = sinkCondition;
   362                         if (status == NS_BASE_STREAM_CLOSED)
   363                             status = NS_OK;
   364                     } else {
   365                         status = cancelStatus; 
   366                     }
   367                     mCallback(mClosure, status);
   368                 }
   369                 break;
   370             }
   371         }
   372     }
   374     nsresult Cancel(nsresult aReason)
   375     {
   376         MutexAutoLock lock(mLock);
   377         if (mCanceled)
   378             return NS_ERROR_FAILURE;
   380         if (NS_SUCCEEDED(aReason)) {
   381             NS_WARNING("cancel with non-failure status code");
   382             aReason = NS_BASE_STREAM_CLOSED;
   383         }
   385         mCanceled = true;
   386         mCancelStatus = aReason;
   387         return NS_OK;
   388     }
   390     NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *source)
   391     {
   392         PostContinuationEvent();
   393         return NS_OK;
   394     }
   396     NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *sink)
   397     {
   398         PostContinuationEvent();
   399         return NS_OK;
   400     }
   402     // continuation event handler
   403     NS_IMETHOD Run()
   404     {
   405         Process();
   407         // clear "in process" flag and post any pending continuation event
   408         MutexAutoLock lock(mLock);
   409         mEventInProcess = false;
   410         if (mEventIsPending) {
   411             mEventIsPending = false;
   412             PostContinuationEvent_Locked();
   413         }
   415         return NS_OK;
   416     }
   418     nsresult PostContinuationEvent()
   419     {
   420         // we cannot post a continuation event if there is currently
   421         // an event in process.  doing so could result in Process being
   422         // run simultaneously on multiple threads, so we mark the event
   423         // as pending, and if an event is already in process then we 
   424         // just let that existing event take care of posting the real
   425         // continuation event.
   427         MutexAutoLock lock(mLock);
   428         return PostContinuationEvent_Locked();
   429     }
   431     nsresult PostContinuationEvent_Locked()
   432     {
   433         nsresult rv = NS_OK;
   434         if (mEventInProcess)
   435             mEventIsPending = true;
   436         else {
   437             rv = mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
   438             if (NS_SUCCEEDED(rv))
   439                 mEventInProcess = true;
   440             else
   441                 NS_WARNING("unable to post continuation event");
   442         }
   443         return rv;
   444     }
   446 protected:
   447     nsCOMPtr<nsIInputStream>       mSource;
   448     nsCOMPtr<nsIOutputStream>      mSink;
   449     nsCOMPtr<nsIAsyncInputStream>  mAsyncSource;
   450     nsCOMPtr<nsIAsyncOutputStream> mAsyncSink;
   451     nsCOMPtr<nsIEventTarget>       mTarget;
   452     Mutex                          mLock;
   453     nsAsyncCopyCallbackFun         mCallback;
   454     nsAsyncCopyProgressFun         mProgressCallback;
   455     void                          *mClosure;
   456     uint32_t                       mChunkSize;
   457     bool                           mEventInProcess;
   458     bool                           mEventIsPending;
   459     bool                           mCloseSource;
   460     bool                           mCloseSink;
   461     bool                           mCanceled;
   462     nsresult                       mCancelStatus;
   463 };
   465 NS_IMPL_ISUPPORTS(nsAStreamCopier,
   466                   nsIInputStreamCallback,
   467                   nsIOutputStreamCallback,
   468                   nsIRunnable)
   470 class nsStreamCopierIB MOZ_FINAL : public nsAStreamCopier
   471 {
   472 public:
   473     nsStreamCopierIB() : nsAStreamCopier() {}
   474     virtual ~nsStreamCopierIB() {}
   476     struct ReadSegmentsState {
   477         nsIOutputStream *mSink;
   478         nsresult         mSinkCondition;
   479     };
   481     static NS_METHOD ConsumeInputBuffer(nsIInputStream *inStr,
   482                                         void *closure,
   483                                         const char *buffer,
   484                                         uint32_t offset,
   485                                         uint32_t count,
   486                                         uint32_t *countWritten)
   487     {
   488         ReadSegmentsState *state = (ReadSegmentsState *) closure;
   490         nsresult rv = state->mSink->Write(buffer, count, countWritten);
   491         if (NS_FAILED(rv))
   492             state->mSinkCondition = rv;
   493         else if (*countWritten == 0)
   494             state->mSinkCondition = NS_BASE_STREAM_CLOSED;
   496         return state->mSinkCondition;
   497     }
   499     uint32_t DoCopy(nsresult *sourceCondition, nsresult *sinkCondition)
   500     {
   501         ReadSegmentsState state;
   502         state.mSink = mSink;
   503         state.mSinkCondition = NS_OK;
   505         uint32_t n;
   506         *sourceCondition =
   507             mSource->ReadSegments(ConsumeInputBuffer, &state, mChunkSize, &n);
   508         *sinkCondition = state.mSinkCondition;
   509         return n;
   510     }
   511 };
   513 class nsStreamCopierOB MOZ_FINAL : public nsAStreamCopier
   514 {
   515 public:
   516     nsStreamCopierOB() : nsAStreamCopier() {}
   517     virtual ~nsStreamCopierOB() {}
   519     struct WriteSegmentsState {
   520         nsIInputStream *mSource;
   521         nsresult        mSourceCondition;
   522     };
   524     static NS_METHOD FillOutputBuffer(nsIOutputStream *outStr,
   525                                       void *closure,
   526                                       char *buffer,
   527                                       uint32_t offset,
   528                                       uint32_t count,
   529                                       uint32_t *countRead)
   530     {
   531         WriteSegmentsState *state = (WriteSegmentsState *) closure;
   533         nsresult rv = state->mSource->Read(buffer, count, countRead);
   534         if (NS_FAILED(rv))
   535             state->mSourceCondition = rv;
   536         else if (*countRead == 0)
   537             state->mSourceCondition = NS_BASE_STREAM_CLOSED;
   539         return state->mSourceCondition;
   540     }
   542     uint32_t DoCopy(nsresult *sourceCondition, nsresult *sinkCondition)
   543     {
   544         WriteSegmentsState state;
   545         state.mSource = mSource;
   546         state.mSourceCondition = NS_OK;
   548         uint32_t n;
   549         *sinkCondition =
   550             mSink->WriteSegments(FillOutputBuffer, &state, mChunkSize, &n);
   551         *sourceCondition = state.mSourceCondition;
   552         return n;
   553     }
   554 };
   556 //-----------------------------------------------------------------------------
   558 nsresult
   559 NS_AsyncCopy(nsIInputStream         *source,
   560              nsIOutputStream        *sink,
   561              nsIEventTarget         *target,
   562              nsAsyncCopyMode         mode,
   563              uint32_t                chunkSize,
   564              nsAsyncCopyCallbackFun  callback,
   565              void                   *closure,
   566              bool                    closeSource,
   567              bool                    closeSink,
   568              nsISupports           **aCopierCtx,
   569              nsAsyncCopyProgressFun  progressCallback)
   570 {
   571     NS_ASSERTION(target, "non-null target required");
   573     nsresult rv;
   574     nsAStreamCopier *copier;
   576     if (mode == NS_ASYNCCOPY_VIA_READSEGMENTS)
   577         copier = new nsStreamCopierIB();
   578     else
   579         copier = new nsStreamCopierOB();
   581     if (!copier)
   582         return NS_ERROR_OUT_OF_MEMORY;
   584     // Start() takes an owning ref to the copier...
   585     NS_ADDREF(copier);
   586     rv = copier->Start(source, sink, target, callback, closure, chunkSize,
   587                        closeSource, closeSink, progressCallback);
   589     if (aCopierCtx) {
   590         *aCopierCtx = static_cast<nsISupports*>(
   591                       static_cast<nsIRunnable*>(copier));
   592         NS_ADDREF(*aCopierCtx);
   593     }
   594     NS_RELEASE(copier);
   596     return rv;
   597 }
   599 //-----------------------------------------------------------------------------
   601 nsresult
   602 NS_CancelAsyncCopy(nsISupports *aCopierCtx, nsresult aReason)
   603 {
   604   nsAStreamCopier *copier = static_cast<nsAStreamCopier *>(
   605                             static_cast<nsIRunnable *>(aCopierCtx));
   606   return copier->Cancel(aReason);
   607 }
   609 //-----------------------------------------------------------------------------
   611 nsresult
   612 NS_ConsumeStream(nsIInputStream *stream, uint32_t maxCount, nsACString &result)
   613 {
   614     nsresult rv = NS_OK;
   615     result.Truncate();
   617     while (maxCount) {
   618         uint64_t avail64;
   619         rv = stream->Available(&avail64);
   620         if (NS_FAILED(rv)) {
   621             if (rv == NS_BASE_STREAM_CLOSED)
   622                 rv = NS_OK;
   623             break;
   624         }
   625         if (avail64 == 0)
   626             break;
   628         uint32_t avail = (uint32_t)XPCOM_MIN<uint64_t>(avail64, maxCount);
   630         // resize result buffer
   631         uint32_t length = result.Length();
   632         if (avail > UINT32_MAX - length)
   633             return NS_ERROR_FILE_TOO_BIG;
   635         result.SetLength(length + avail);
   636         if (result.Length() != (length + avail))
   637             return NS_ERROR_OUT_OF_MEMORY;
   638         char *buf = result.BeginWriting() + length;
   640         uint32_t n;
   641         rv = stream->Read(buf, avail, &n);
   642         if (NS_FAILED(rv))
   643             break;
   644         if (n != avail)
   645             result.SetLength(length + n);
   646         if (n == 0)
   647             break;
   648         maxCount -= n;
   649     }
   651     return rv;
   652 }
   654 //-----------------------------------------------------------------------------
   656 static NS_METHOD
   657 TestInputStream(nsIInputStream *inStr,
   658                 void *closure,
   659                 const char *buffer,
   660                 uint32_t offset,
   661                 uint32_t count,
   662                 uint32_t *countWritten)
   663 {
   664     bool *result = static_cast<bool *>(closure);
   665     *result = true;
   666     return NS_ERROR_ABORT;  // don't call me anymore
   667 }
   669 bool
   670 NS_InputStreamIsBuffered(nsIInputStream *stream)
   671 {
   672     nsCOMPtr<nsIBufferedInputStream> bufferedIn = do_QueryInterface(stream);
   673     if (bufferedIn) {
   674         return true;
   675     }
   677     bool result = false;
   678     uint32_t n;
   679     nsresult rv = stream->ReadSegments(TestInputStream,
   680                                        &result, 1, &n);
   681     return result || NS_SUCCEEDED(rv);
   682 }
   684 static NS_METHOD
   685 TestOutputStream(nsIOutputStream *outStr,
   686                  void *closure,
   687                  char *buffer,
   688                  uint32_t offset,
   689                  uint32_t count,
   690                  uint32_t *countRead)
   691 {
   692     bool *result = static_cast<bool *>(closure);
   693     *result = true;
   694     return NS_ERROR_ABORT;  // don't call me anymore
   695 }
   697 bool
   698 NS_OutputStreamIsBuffered(nsIOutputStream *stream)
   699 {
   700     nsCOMPtr<nsIBufferedOutputStream> bufferedOut = do_QueryInterface(stream);
   701     if (bufferedOut) {
   702         return true;
   703     }
   705     bool result = false;
   706     uint32_t n;
   707     stream->WriteSegments(TestOutputStream, &result, 1, &n);
   708     return result;
   709 }
   711 //-----------------------------------------------------------------------------
   713 NS_METHOD
   714 NS_CopySegmentToStream(nsIInputStream *inStr,
   715                        void *closure,
   716                        const char *buffer,
   717                        uint32_t offset,
   718                        uint32_t count,
   719                        uint32_t *countWritten)
   720 {
   721     nsIOutputStream *outStr = static_cast<nsIOutputStream *>(closure);
   722     *countWritten = 0;
   723     while (count) {
   724         uint32_t n;
   725         nsresult rv = outStr->Write(buffer, count, &n);
   726         if (NS_FAILED(rv))
   727             return rv;
   728         buffer += n;
   729         count -= n;
   730         *countWritten += n;
   731     }
   732     return NS_OK;
   733 }
   735 NS_METHOD
   736 NS_CopySegmentToBuffer(nsIInputStream *inStr,
   737                        void *closure,
   738                        const char *buffer,
   739                        uint32_t offset,
   740                        uint32_t count,
   741                        uint32_t *countWritten)
   742 {
   743     char *toBuf = static_cast<char *>(closure);
   744     memcpy(&toBuf[offset], buffer, count);
   745     *countWritten = count;
   746     return NS_OK;
   747 }
   749 NS_METHOD
   750 NS_CopySegmentToBuffer(nsIOutputStream *outStr,
   751                        void *closure,
   752                        char *buffer,
   753                        uint32_t offset,
   754                        uint32_t count,
   755                        uint32_t *countRead)
   756 {
   757     const char* fromBuf = static_cast<const char*>(closure);
   758     memcpy(buffer, &fromBuf[offset], count);
   759     *countRead = count;
   760     return NS_OK;
   761 }
   763 NS_METHOD
   764 NS_DiscardSegment(nsIInputStream *inStr,
   765                   void *closure,
   766                   const char *buffer,
   767                   uint32_t offset,
   768                   uint32_t count,
   769                   uint32_t *countWritten)
   770 {
   771     *countWritten = count;
   772     return NS_OK;
   773 }
   775 //-----------------------------------------------------------------------------
   777 NS_METHOD
   778 NS_WriteSegmentThunk(nsIInputStream *inStr,
   779                      void *closure,
   780                      const char *buffer,
   781                      uint32_t offset,
   782                      uint32_t count,
   783                      uint32_t *countWritten)
   784 {
   785     nsWriteSegmentThunk *thunk = static_cast<nsWriteSegmentThunk *>(closure);
   786     return thunk->mFun(thunk->mStream, thunk->mClosure, buffer, offset, count,
   787                        countWritten);
   788 }
   790 NS_METHOD
   791 NS_FillArray(FallibleTArray<char>& aDest, nsIInputStream *aInput,
   792              uint32_t aKeep, uint32_t *aNewBytes)
   793 {
   794   MOZ_ASSERT(aInput, "null stream");
   795   MOZ_ASSERT(aKeep <= aDest.Length(), "illegal keep count");
   797   char* aBuffer = aDest.Elements();
   798   int64_t keepOffset = int64_t(aDest.Length()) - aKeep;
   799   if (0 != aKeep && keepOffset > 0) {
   800     memmove(aBuffer, aBuffer + keepOffset, aKeep);
   801   }
   803   nsresult rv =
   804     aInput->Read(aBuffer + aKeep, aDest.Capacity() - aKeep, aNewBytes);
   805   if (NS_FAILED(rv)) {
   806     *aNewBytes = 0;
   807   }
   808   // NOTE: we rely on the fact that the new slots are NOT initialized by
   809   // SetLengthAndRetainStorage here, see nsTArrayElementTraits::Construct()
   810   // in nsTArray.h:
   811   aDest.SetLengthAndRetainStorage(aKeep + *aNewBytes);
   813   MOZ_ASSERT(aDest.Length() <= aDest.Capacity(), "buffer overflow");
   814   return rv;
   815 }

mercurial