netwerk/base/src/nsBufferedStreams.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     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 "ipc/IPCMessageUtils.h"
     8 #include "nsBufferedStreams.h"
     9 #include "nsStreamUtils.h"
    10 #include "nsNetCID.h"
    11 #include "nsIClassInfoImpl.h"
    12 #include "mozilla/ipc/InputStreamUtils.h"
    13 #include <algorithm>
    15 #ifdef DEBUG_brendan
    16 # define METERING
    17 #endif
    19 #ifdef METERING
    20 # include <stdio.h>
    21 # define METER(x)       x
    22 # define MAX_BIG_SEEKS  20
    24 static struct {
    25     uint32_t            mSeeksWithinBuffer;
    26     uint32_t            mSeeksOutsideBuffer;
    27     uint32_t            mBufferReadUponSeek;
    28     uint32_t            mBufferUnreadUponSeek;
    29     uint32_t            mBytesReadFromBuffer;
    30     uint32_t            mBigSeekIndex;
    31     struct {
    32         int64_t         mOldOffset;
    33         int64_t         mNewOffset;
    34     } mBigSeek[MAX_BIG_SEEKS];
    35 } bufstats;
    36 #else
    37 # define METER(x)       /* nothing */
    38 #endif
    40 using namespace mozilla::ipc;
    42 ////////////////////////////////////////////////////////////////////////////////
    43 // nsBufferedStream
    45 nsBufferedStream::nsBufferedStream()
    46     : mBuffer(nullptr),
    47       mBufferStartOffset(0),
    48       mCursor(0), 
    49       mFillPoint(0),
    50       mStream(nullptr),
    51       mBufferDisabled(false),
    52       mEOF(false),
    53       mGetBufferCount(0)
    54 {
    55 }
    57 nsBufferedStream::~nsBufferedStream()
    58 {
    59     Close();
    60 }
    62 NS_IMPL_ISUPPORTS(nsBufferedStream, nsISeekableStream)
    64 nsresult
    65 nsBufferedStream::Init(nsISupports* stream, uint32_t bufferSize)
    66 {
    67     NS_ASSERTION(stream, "need to supply a stream");
    68     NS_ASSERTION(mStream == nullptr, "already inited");
    69     mStream = stream;
    70     NS_IF_ADDREF(mStream);
    71     mBufferSize = bufferSize;
    72     mBufferStartOffset = 0;
    73     mCursor = 0;
    74     const mozilla::fallible_t fallible = mozilla::fallible_t();
    75     mBuffer = new (fallible) char[bufferSize];
    76     if (mBuffer == nullptr)
    77         return NS_ERROR_OUT_OF_MEMORY;
    78     return NS_OK;
    79 }
    81 nsresult
    82 nsBufferedStream::Close()
    83 {
    84     NS_IF_RELEASE(mStream);
    85     if (mBuffer) {
    86         delete[] mBuffer;
    87         mBuffer = nullptr;
    88         mBufferSize = 0;
    89         mBufferStartOffset = 0;
    90         mCursor = 0;
    91         mFillPoint = 0;
    92     }
    93 #ifdef METERING
    94     {
    95         static FILE *tfp;
    96         if (!tfp) {
    97             tfp = fopen("/tmp/bufstats", "w");
    98             if (tfp)
    99                 setvbuf(tfp, nullptr, _IOLBF, 0);
   100         }
   101         if (tfp) {
   102             fprintf(tfp, "seeks within buffer:    %u\n",
   103                     bufstats.mSeeksWithinBuffer);
   104             fprintf(tfp, "seeks outside buffer:   %u\n",
   105                     bufstats.mSeeksOutsideBuffer);
   106             fprintf(tfp, "buffer read on seek:    %u\n",
   107                     bufstats.mBufferReadUponSeek);
   108             fprintf(tfp, "buffer unread on seek:  %u\n",
   109                     bufstats.mBufferUnreadUponSeek);
   110             fprintf(tfp, "bytes read from buffer: %u\n",
   111                     bufstats.mBytesReadFromBuffer);
   112             for (uint32_t i = 0; i < bufstats.mBigSeekIndex; i++) {
   113                 fprintf(tfp, "bigseek[%u] = {old: %u, new: %u}\n",
   114                         i,
   115                         bufstats.mBigSeek[i].mOldOffset,
   116                         bufstats.mBigSeek[i].mNewOffset);
   117             }
   118         }
   119     }
   120 #endif
   121     return NS_OK;
   122 }
   124 NS_IMETHODIMP
   125 nsBufferedStream::Seek(int32_t whence, int64_t offset)
   126 {
   127     if (mStream == nullptr)
   128         return NS_BASE_STREAM_CLOSED;
   130     // If the underlying stream isn't a random access store, then fail early.
   131     // We could possibly succeed for the case where the seek position denotes
   132     // something that happens to be read into the buffer, but that would make
   133     // the failure data-dependent.
   134     nsresult rv;
   135     nsCOMPtr<nsISeekableStream> ras = do_QueryInterface(mStream, &rv);
   136     if (NS_FAILED(rv)) return rv;
   138     int64_t absPos = 0;
   139     switch (whence) {
   140       case nsISeekableStream::NS_SEEK_SET:
   141         absPos = offset;
   142         break;
   143       case nsISeekableStream::NS_SEEK_CUR:
   144         absPos = mBufferStartOffset;
   145         absPos += mCursor;
   146         absPos += offset;
   147         break;
   148       case nsISeekableStream::NS_SEEK_END:
   149         absPos = -1;
   150         break;
   151       default:
   152         NS_NOTREACHED("bogus seek whence parameter");
   153         return NS_ERROR_UNEXPECTED;
   154     }
   156     // Let mCursor point into the existing buffer if the new position is
   157     // between the current cursor and the mFillPoint "fencepost" -- the
   158     // client may never get around to a Read or Write after this Seek.
   159     // Read and Write worry about flushing and filling in that event.
   160     // But if we're at EOF, make sure to pass the seek through to the
   161     // underlying stream, because it may have auto-closed itself and
   162     // needs to reopen.
   163     uint32_t offsetInBuffer = uint32_t(absPos - mBufferStartOffset);
   164     if (offsetInBuffer <= mFillPoint && !mEOF) {
   165         METER(bufstats.mSeeksWithinBuffer++);
   166         mCursor = offsetInBuffer;
   167         return NS_OK;
   168     }
   170     METER(bufstats.mSeeksOutsideBuffer++);
   171     METER(bufstats.mBufferReadUponSeek += mCursor);
   172     METER(bufstats.mBufferUnreadUponSeek += mFillPoint - mCursor);
   173     rv = Flush();
   174     if (NS_FAILED(rv)) return rv;
   176     rv = ras->Seek(whence, offset);
   177     if (NS_FAILED(rv)) return rv;
   179     mEOF = false;
   181     // Recompute whether the offset we're seeking to is in our buffer.
   182     // Note that we need to recompute because Flush() might have
   183     // changed mBufferStartOffset.
   184     offsetInBuffer = uint32_t(absPos - mBufferStartOffset);
   185     if (offsetInBuffer <= mFillPoint) {
   186         // It's safe to just set mCursor to offsetInBuffer.  In particular, we
   187         // want to avoid calling Fill() here since we already have the data that
   188         // was seeked to and calling Fill() might auto-close our underlying
   189         // stream in some cases.
   190         mCursor = offsetInBuffer;
   191         return NS_OK;
   192     }
   194     METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS)
   195               bufstats.mBigSeek[bufstats.mBigSeekIndex].mOldOffset =
   196                   mBufferStartOffset + int64_t(mCursor));
   197     const int64_t minus1 = -1;
   198     if (absPos == minus1) {
   199         // then we had the SEEK_END case, above
   200         int64_t tellPos;
   201         rv = ras->Tell(&tellPos);
   202         mBufferStartOffset = tellPos;
   203         if (NS_FAILED(rv)) return rv;
   204     }
   205     else {
   206         mBufferStartOffset = absPos;
   207     }
   208     METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS)
   209               bufstats.mBigSeek[bufstats.mBigSeekIndex++].mNewOffset =
   210                   mBufferStartOffset);
   212     mFillPoint = mCursor = 0;
   213     return Fill();
   214 }
   216 NS_IMETHODIMP
   217 nsBufferedStream::Tell(int64_t *result)
   218 {
   219     if (mStream == nullptr)
   220         return NS_BASE_STREAM_CLOSED;
   222     int64_t result64 = mBufferStartOffset;
   223     result64 += mCursor;
   224     *result = result64;
   225     return NS_OK;
   226 }
   228 NS_IMETHODIMP
   229 nsBufferedStream::SetEOF()
   230 {
   231     if (mStream == nullptr)
   232         return NS_BASE_STREAM_CLOSED;
   234     nsresult rv;
   235     nsCOMPtr<nsISeekableStream> ras = do_QueryInterface(mStream, &rv);
   236     if (NS_FAILED(rv)) return rv;
   238     rv = ras->SetEOF();
   239     if (NS_SUCCEEDED(rv))
   240         mEOF = true;
   241     return rv;
   242 }
   244 ////////////////////////////////////////////////////////////////////////////////
   245 // nsBufferedInputStream
   247 NS_IMPL_ADDREF_INHERITED(nsBufferedInputStream, nsBufferedStream)
   248 NS_IMPL_RELEASE_INHERITED(nsBufferedInputStream, nsBufferedStream)
   250 NS_IMPL_CLASSINFO(nsBufferedInputStream, nullptr, nsIClassInfo::THREADSAFE,
   251                   NS_BUFFEREDINPUTSTREAM_CID)
   253 NS_INTERFACE_MAP_BEGIN(nsBufferedInputStream)
   254     NS_INTERFACE_MAP_ENTRY(nsIInputStream)
   255     NS_INTERFACE_MAP_ENTRY(nsIBufferedInputStream)
   256     NS_INTERFACE_MAP_ENTRY(nsIStreamBufferAccess)
   257     NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
   258     NS_IMPL_QUERY_CLASSINFO(nsBufferedInputStream)
   259 NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream)
   261 NS_IMPL_CI_INTERFACE_GETTER(nsBufferedInputStream,
   262                             nsIInputStream,
   263                             nsIBufferedInputStream,
   264                             nsISeekableStream,
   265                             nsIStreamBufferAccess)
   267 nsresult
   268 nsBufferedInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
   269 {
   270     NS_ENSURE_NO_AGGREGATION(aOuter);
   272     nsBufferedInputStream* stream = new nsBufferedInputStream();
   273     if (stream == nullptr)
   274         return NS_ERROR_OUT_OF_MEMORY;
   275     NS_ADDREF(stream);
   276     nsresult rv = stream->QueryInterface(aIID, aResult);
   277     NS_RELEASE(stream);
   278     return rv;
   279 }
   281 NS_IMETHODIMP
   282 nsBufferedInputStream::Init(nsIInputStream* stream, uint32_t bufferSize)
   283 {
   284     return nsBufferedStream::Init(stream, bufferSize);
   285 }
   287 NS_IMETHODIMP
   288 nsBufferedInputStream::Close()
   289 {
   290     nsresult rv1 = NS_OK, rv2;
   291     if (mStream) {
   292         rv1 = Source()->Close();
   293         NS_RELEASE(mStream);
   294     }
   295     rv2 = nsBufferedStream::Close();
   296     if (NS_FAILED(rv1)) return rv1;
   297     return rv2;
   298 }
   300 NS_IMETHODIMP
   301 nsBufferedInputStream::Available(uint64_t *result)
   302 {
   303     nsresult rv = NS_OK;
   304     *result = 0;
   305     if (mStream) {
   306         rv = Source()->Available(result);
   307     }
   308     *result += (mFillPoint - mCursor);
   309     return rv;
   310 }
   312 NS_IMETHODIMP
   313 nsBufferedInputStream::Read(char * buf, uint32_t count, uint32_t *result)
   314 {
   315     if (mBufferDisabled) {
   316         if (!mStream) {
   317             *result = 0;
   318             return NS_OK;
   319         }
   320         nsresult rv = Source()->Read(buf, count, result);
   321         if (NS_SUCCEEDED(rv)) {
   322             mBufferStartOffset += *result;  // so nsBufferedStream::Tell works
   323             if (*result == 0) {
   324                 mEOF = true;
   325             }
   326         }
   327         return rv;
   328     }
   330     return ReadSegments(NS_CopySegmentToBuffer, buf, count, result);
   331 }
   333 NS_IMETHODIMP
   334 nsBufferedInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure,
   335                                     uint32_t count, uint32_t *result)
   336 {
   337     *result = 0;
   339     if (!mStream)
   340         return NS_OK;
   342     nsresult rv = NS_OK;
   343     while (count > 0) {
   344         uint32_t amt = std::min(count, mFillPoint - mCursor);
   345         if (amt > 0) {
   346             uint32_t read = 0;
   347             rv = writer(this, closure, mBuffer + mCursor, *result, amt, &read);
   348             if (NS_FAILED(rv)) {
   349                 // errors returned from the writer end here!
   350                 rv = NS_OK;
   351                 break;
   352             }
   353             *result += read;
   354             count -= read;
   355             mCursor += read;
   356         }
   357         else {
   358             rv = Fill();
   359             if (NS_FAILED(rv) || mFillPoint == mCursor)
   360                 break;
   361         }
   362     }
   363     return (*result > 0) ? NS_OK : rv;
   364 }
   366 NS_IMETHODIMP
   367 nsBufferedInputStream::IsNonBlocking(bool *aNonBlocking)
   368 {
   369     if (mStream)
   370         return Source()->IsNonBlocking(aNonBlocking);
   371     return NS_ERROR_NOT_INITIALIZED;
   372 }
   374 NS_IMETHODIMP
   375 nsBufferedInputStream::Fill()
   376 {
   377     if (mBufferDisabled)
   378         return NS_OK;
   379     NS_ENSURE_TRUE(mStream, NS_ERROR_NOT_INITIALIZED);
   381     nsresult rv;
   382     int32_t rem = int32_t(mFillPoint - mCursor);
   383     if (rem > 0) {
   384         // slide the remainder down to the start of the buffer
   385         // |<------------->|<--rem-->|<--->|
   386         // b               c         f     s
   387         memcpy(mBuffer, mBuffer + mCursor, rem);
   388     }
   389     mBufferStartOffset += mCursor;
   390     mFillPoint = rem;
   391     mCursor = 0;
   393     uint32_t amt;
   394     rv = Source()->Read(mBuffer + mFillPoint, mBufferSize - mFillPoint, &amt);
   395     if (NS_FAILED(rv)) return rv;
   397     if (amt == 0)
   398         mEOF = true;
   400     mFillPoint += amt;
   401     return NS_OK;
   402 }
   404 NS_IMETHODIMP_(char*)
   405 nsBufferedInputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
   406 {
   407     NS_ASSERTION(mGetBufferCount == 0, "nested GetBuffer!");
   408     if (mGetBufferCount != 0)
   409         return nullptr;
   411     if (mBufferDisabled)
   412         return nullptr;
   414     char* buf = mBuffer + mCursor;
   415     uint32_t rem = mFillPoint - mCursor;
   416     if (rem == 0) {
   417         if (NS_FAILED(Fill()))
   418             return nullptr;
   419         buf = mBuffer + mCursor;
   420         rem = mFillPoint - mCursor;
   421     }
   423     uint32_t mod = (NS_PTR_TO_INT32(buf) & aAlignMask);
   424     if (mod) {
   425         uint32_t pad = aAlignMask + 1 - mod;
   426         if (pad > rem)
   427             return nullptr;
   429         memset(buf, 0, pad);
   430         mCursor += pad;
   431         buf += pad;
   432         rem -= pad;
   433     }
   435     if (aLength > rem)
   436         return nullptr;
   437     mGetBufferCount++;
   438     return buf;
   439 }
   441 NS_IMETHODIMP_(void)
   442 nsBufferedInputStream::PutBuffer(char* aBuffer, uint32_t aLength)
   443 {
   444     NS_ASSERTION(mGetBufferCount == 1, "stray PutBuffer!");
   445     if (--mGetBufferCount != 0)
   446         return;
   448     NS_ASSERTION(mCursor + aLength <= mFillPoint, "PutBuffer botch");
   449     mCursor += aLength;
   450 }
   452 NS_IMETHODIMP
   453 nsBufferedInputStream::DisableBuffering()
   454 {
   455     NS_ASSERTION(!mBufferDisabled, "redundant call to DisableBuffering!");
   456     NS_ASSERTION(mGetBufferCount == 0,
   457                  "DisableBuffer call between GetBuffer and PutBuffer!");
   458     if (mGetBufferCount != 0)
   459         return NS_ERROR_UNEXPECTED;
   461     // Empty the buffer so nsBufferedStream::Tell works.
   462     mBufferStartOffset += mCursor;
   463     mFillPoint = mCursor = 0;
   464     mBufferDisabled = true;
   465     return NS_OK;
   466 }
   468 NS_IMETHODIMP
   469 nsBufferedInputStream::EnableBuffering()
   470 {
   471     NS_ASSERTION(mBufferDisabled, "gratuitous call to EnableBuffering!");
   472     mBufferDisabled = false;
   473     return NS_OK;
   474 }
   476 NS_IMETHODIMP
   477 nsBufferedInputStream::GetUnbufferedStream(nsISupports* *aStream)
   478 {
   479     // Empty the buffer so subsequent i/o trumps any buffered data.
   480     mBufferStartOffset += mCursor;
   481     mFillPoint = mCursor = 0;
   483     *aStream = mStream;
   484     NS_IF_ADDREF(*aStream);
   485     return NS_OK;
   486 }
   488 void
   489 nsBufferedInputStream::Serialize(InputStreamParams& aParams,
   490                                  FileDescriptorArray& aFileDescriptors)
   491 {
   492     BufferedInputStreamParams params;
   494     if (mStream) {
   495         nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream);
   496         MOZ_ASSERT(stream);
   498         InputStreamParams wrappedParams;
   499         SerializeInputStream(stream, wrappedParams, aFileDescriptors);
   501         params.optionalStream() = wrappedParams;
   502     }
   503     else {
   504         params.optionalStream() = mozilla::void_t();
   505     }
   507     params.bufferSize() = mBufferSize;
   509     aParams = params;
   510 }
   512 bool
   513 nsBufferedInputStream::Deserialize(const InputStreamParams& aParams,
   514                                    const FileDescriptorArray& aFileDescriptors)
   515 {
   516     if (aParams.type() != InputStreamParams::TBufferedInputStreamParams) {
   517         NS_ERROR("Received unknown parameters from the other process!");
   518         return false;
   519     }
   521     const BufferedInputStreamParams& params =
   522         aParams.get_BufferedInputStreamParams();
   523     const OptionalInputStreamParams& wrappedParams = params.optionalStream();
   525     nsCOMPtr<nsIInputStream> stream;
   526     if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) {
   527         stream = DeserializeInputStream(wrappedParams.get_InputStreamParams(),
   528                                         aFileDescriptors);
   529         if (!stream) {
   530             NS_WARNING("Failed to deserialize wrapped stream!");
   531             return false;
   532         }
   533     }
   534     else {
   535         NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t,
   536                      "Unknown type for OptionalInputStreamParams!");
   537     }
   539     nsresult rv = Init(stream, params.bufferSize());
   540     NS_ENSURE_SUCCESS(rv, false);
   542     return true;
   543 }
   545 ////////////////////////////////////////////////////////////////////////////////
   546 // nsBufferedOutputStream
   548 NS_IMPL_ADDREF_INHERITED(nsBufferedOutputStream, nsBufferedStream)
   549 NS_IMPL_RELEASE_INHERITED(nsBufferedOutputStream, nsBufferedStream)
   550 // This QI uses NS_INTERFACE_MAP_ENTRY_CONDITIONAL to check for
   551 // non-nullness of mSafeStream.
   552 NS_INTERFACE_MAP_BEGIN(nsBufferedOutputStream)
   553     NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
   554     NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISafeOutputStream, mSafeStream)
   555     NS_INTERFACE_MAP_ENTRY(nsIBufferedOutputStream)
   556     NS_INTERFACE_MAP_ENTRY(nsIStreamBufferAccess)
   557 NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream)
   559 nsresult
   560 nsBufferedOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
   561 {
   562     NS_ENSURE_NO_AGGREGATION(aOuter);
   564     nsBufferedOutputStream* stream = new nsBufferedOutputStream();
   565     if (stream == nullptr)
   566         return NS_ERROR_OUT_OF_MEMORY;
   567     NS_ADDREF(stream);
   568     nsresult rv = stream->QueryInterface(aIID, aResult);
   569     NS_RELEASE(stream);
   570     return rv;
   571 }
   573 NS_IMETHODIMP
   574 nsBufferedOutputStream::Init(nsIOutputStream* stream, uint32_t bufferSize)
   575 {
   576     // QI stream to an nsISafeOutputStream, to see if we should support it
   577     mSafeStream = do_QueryInterface(stream);
   579     return nsBufferedStream::Init(stream, bufferSize);
   580 }
   582 NS_IMETHODIMP
   583 nsBufferedOutputStream::Close()
   584 {
   585     nsresult rv1, rv2 = NS_OK, rv3;
   586     rv1 = Flush();
   587     // If we fail to Flush all the data, then we close anyway and drop the
   588     // remaining data in the buffer. We do this because it's what Unix does
   589     // for fclose and close. However, we report the error from Flush anyway.
   590     if (mStream) {
   591         rv2 = Sink()->Close();
   592         NS_RELEASE(mStream);
   593     }
   594     rv3 = nsBufferedStream::Close();
   595     if (NS_FAILED(rv1)) return rv1;
   596     if (NS_FAILED(rv2)) return rv2;
   597     return rv3;
   598 }
   600 NS_IMETHODIMP
   601 nsBufferedOutputStream::Write(const char *buf, uint32_t count, uint32_t *result)
   602 {
   603     nsresult rv = NS_OK;
   604     uint32_t written = 0;
   605     while (count > 0) {
   606         uint32_t amt = std::min(count, mBufferSize - mCursor);
   607         if (amt > 0) {
   608             memcpy(mBuffer + mCursor, buf + written, amt);
   609             written += amt;
   610             count -= amt;
   611             mCursor += amt;
   612             if (mFillPoint < mCursor)
   613                 mFillPoint = mCursor;
   614         }
   615         else {
   616             NS_ASSERTION(mFillPoint, "iloop in nsBufferedOutputStream::Write!");
   617             rv = Flush();
   618             if (NS_FAILED(rv)) break;
   619         }
   620     }
   621     *result = written;
   622     return (written > 0) ? NS_OK : rv;
   623 }
   625 NS_IMETHODIMP
   626 nsBufferedOutputStream::Flush()
   627 {
   628     nsresult rv;
   629     uint32_t amt;
   630     if (!mStream) {
   631         // Stream already cancelled/flushed; probably because of previous error.
   632         return NS_OK;
   633     }
   634     rv = Sink()->Write(mBuffer, mFillPoint, &amt);
   635     if (NS_FAILED(rv)) return rv;
   636     mBufferStartOffset += amt;
   637     if (amt == mFillPoint) {
   638         mFillPoint = mCursor = 0;
   639         return NS_OK;   // flushed everything
   640     }
   642     // slide the remainder down to the start of the buffer
   643     // |<-------------->|<---|----->|
   644     // b                a    c      s
   645     uint32_t rem = mFillPoint - amt;
   646     memcpy(mBuffer, mBuffer + amt, rem);
   647     mFillPoint = mCursor = rem;
   648     return NS_ERROR_FAILURE;        // didn't flush all
   649 }
   651 // nsISafeOutputStream
   652 NS_IMETHODIMP
   653 nsBufferedOutputStream::Finish()
   654 {
   655     // flush the stream, to write out any buffered data...
   656     nsresult rv = nsBufferedOutputStream::Flush();
   657     if (NS_FAILED(rv))
   658         NS_WARNING("failed to flush buffered data! possible dataloss");
   660     // ... and finish the underlying stream...
   661     if (NS_SUCCEEDED(rv))
   662         rv = mSafeStream->Finish();
   663     else
   664         Sink()->Close();
   666     // ... and close the buffered stream, so any further attempts to flush/close
   667     // the buffered stream won't cause errors.
   668     nsBufferedStream::Close();
   670     return rv;
   671 }
   673 static NS_METHOD
   674 nsReadFromInputStream(nsIOutputStream* outStr,
   675                       void* closure,
   676                       char* toRawSegment, 
   677                       uint32_t offset,
   678                       uint32_t count,
   679                       uint32_t *readCount)
   680 {
   681     nsIInputStream* fromStream = (nsIInputStream*)closure;
   682     return fromStream->Read(toRawSegment, count, readCount);
   683 }
   685 NS_IMETHODIMP
   686 nsBufferedOutputStream::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval)
   687 {
   688     return WriteSegments(nsReadFromInputStream, inStr, count, _retval);
   689 }
   691 NS_IMETHODIMP
   692 nsBufferedOutputStream::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval)
   693 {
   694     *_retval = 0;
   695     nsresult rv;
   696     while (count > 0) {
   697         uint32_t left = std::min(count, mBufferSize - mCursor);
   698         if (left == 0) {
   699             rv = Flush();
   700             if (NS_FAILED(rv))
   701               return rv;
   703             continue;
   704         }
   706         uint32_t read = 0;
   707         rv = reader(this, closure, mBuffer + mCursor, *_retval, left, &read);
   709         if (NS_FAILED(rv)) // If we have written some data, return ok
   710             return (*_retval > 0) ? NS_OK : rv;
   711         mCursor += read;
   712         *_retval += read;
   713         count -= read;
   714         mFillPoint = std::max(mFillPoint, mCursor);
   715     }
   716     return NS_OK;
   717 }
   719 NS_IMETHODIMP
   720 nsBufferedOutputStream::IsNonBlocking(bool *aNonBlocking)
   721 {
   722     if (mStream)
   723         return Sink()->IsNonBlocking(aNonBlocking);
   724     return NS_ERROR_NOT_INITIALIZED;
   725 }
   727 NS_IMETHODIMP_(char*)
   728 nsBufferedOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
   729 {
   730     NS_ASSERTION(mGetBufferCount == 0, "nested GetBuffer!");
   731     if (mGetBufferCount != 0)
   732         return nullptr;
   734     if (mBufferDisabled)
   735         return nullptr;
   737     char* buf = mBuffer + mCursor;
   738     uint32_t rem = mBufferSize - mCursor;
   739     if (rem == 0) {
   740         if (NS_FAILED(Flush()))
   741             return nullptr;
   742         buf = mBuffer + mCursor;
   743         rem = mBufferSize - mCursor;
   744     }
   746     uint32_t mod = (NS_PTR_TO_INT32(buf) & aAlignMask);
   747     if (mod) {
   748         uint32_t pad = aAlignMask + 1 - mod;
   749         if (pad > rem)
   750             return nullptr;
   752         memset(buf, 0, pad);
   753         mCursor += pad;
   754         buf += pad;
   755         rem -= pad;
   756     }
   758     if (aLength > rem)
   759         return nullptr;
   760     mGetBufferCount++;
   761     return buf;
   762 }
   764 NS_IMETHODIMP_(void)
   765 nsBufferedOutputStream::PutBuffer(char* aBuffer, uint32_t aLength)
   766 {
   767     NS_ASSERTION(mGetBufferCount == 1, "stray PutBuffer!");
   768     if (--mGetBufferCount != 0)
   769         return;
   771     NS_ASSERTION(mCursor + aLength <= mBufferSize, "PutBuffer botch");
   772     mCursor += aLength;
   773     if (mFillPoint < mCursor)
   774         mFillPoint = mCursor;
   775 }
   777 NS_IMETHODIMP
   778 nsBufferedOutputStream::DisableBuffering()
   779 {
   780     NS_ASSERTION(!mBufferDisabled, "redundant call to DisableBuffering!");
   781     NS_ASSERTION(mGetBufferCount == 0,
   782                  "DisableBuffer call between GetBuffer and PutBuffer!");
   783     if (mGetBufferCount != 0)
   784         return NS_ERROR_UNEXPECTED;
   786     // Empty the buffer so nsBufferedStream::Tell works.
   787     nsresult rv = Flush();
   788     if (NS_FAILED(rv))
   789         return rv;
   791     mBufferDisabled = true;
   792     return NS_OK;
   793 }
   795 NS_IMETHODIMP
   796 nsBufferedOutputStream::EnableBuffering()
   797 {
   798     NS_ASSERTION(mBufferDisabled, "gratuitous call to EnableBuffering!");
   799     mBufferDisabled = false;
   800     return NS_OK;
   801 }
   803 NS_IMETHODIMP
   804 nsBufferedOutputStream::GetUnbufferedStream(nsISupports* *aStream)
   805 {
   806     // Empty the buffer so subsequent i/o trumps any buffered data.
   807     if (mFillPoint) {
   808         nsresult rv = Flush();
   809         if (NS_FAILED(rv))
   810             return rv;
   811     }
   813     *aStream = mStream;
   814     NS_IF_ADDREF(*aStream);
   815     return NS_OK;
   816 }
   818 ////////////////////////////////////////////////////////////////////////////////

mercurial