Back to index

lightning-sunbird  0.9+nobinonly
nsBufferedStreams.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsBufferedStreams.h"
00039 #include "nsCRT.h"
00040 
00041 #ifdef DEBUG_brendan
00042 # define METERING
00043 #endif
00044 
00045 #ifdef METERING
00046 # include <stdio.h>
00047 # define METER(x)       x
00048 # define MAX_BIG_SEEKS  20
00049 
00050 static struct {
00051     PRUint32            mSeeksWithinBuffer;
00052     PRUint32            mSeeksOutsideBuffer;
00053     PRUint32            mBufferReadUponSeek;
00054     PRUint32            mBufferUnreadUponSeek;
00055     PRUint32            mBytesReadFromBuffer;
00056     PRUint32            mBigSeekIndex;
00057     struct {
00058         PRInt64         mOldOffset;
00059         PRInt64         mNewOffset;
00060     } mBigSeek[MAX_BIG_SEEKS];
00061 } bufstats;
00062 #else
00063 # define METER(x)       /* nothing */
00064 #endif
00065 
00067 // nsBufferedStream
00068 
00069 nsBufferedStream::nsBufferedStream()
00070     : mBuffer(nsnull),
00071       mBufferStartOffset(0),
00072       mCursor(0), 
00073       mFillPoint(0),
00074       mStream(nsnull),
00075       mBufferDisabled(PR_FALSE),
00076       mGetBufferCount(0)
00077 {
00078 }
00079 
00080 nsBufferedStream::~nsBufferedStream()
00081 {
00082     Close();
00083 }
00084 
00085 NS_IMPL_THREADSAFE_ISUPPORTS1(nsBufferedStream, nsISeekableStream)
00086 
00087 nsresult
00088 nsBufferedStream::Init(nsISupports* stream, PRUint32 bufferSize)
00089 {
00090     NS_ASSERTION(stream, "need to supply a stream");
00091     NS_ASSERTION(mStream == nsnull, "already inited");
00092     mStream = stream;
00093     NS_IF_ADDREF(mStream);
00094     mBufferSize = bufferSize;
00095     mBufferStartOffset = 0;
00096     mCursor = 0;
00097     mBuffer = new char[bufferSize];
00098     if (mBuffer == nsnull)
00099         return NS_ERROR_OUT_OF_MEMORY;
00100     return NS_OK;
00101 }
00102 
00103 nsresult
00104 nsBufferedStream::Close()
00105 {
00106     NS_IF_RELEASE(mStream);
00107     if (mBuffer) {
00108         delete[] mBuffer;
00109         mBuffer = nsnull;
00110         mBufferSize = 0;
00111         mBufferStartOffset = 0;
00112         mCursor = 0;
00113         mFillPoint = 0;
00114     }
00115 #ifdef METERING
00116     {
00117         static FILE *tfp;
00118         if (!tfp) {
00119             tfp = fopen("/tmp/bufstats", "w");
00120             if (tfp)
00121                 setvbuf(tfp, NULL, _IOLBF, 0);
00122         }
00123         if (tfp) {
00124             fprintf(tfp, "seeks within buffer:    %u\n",
00125                     bufstats.mSeeksWithinBuffer);
00126             fprintf(tfp, "seeks outside buffer:   %u\n",
00127                     bufstats.mSeeksOutsideBuffer);
00128             fprintf(tfp, "buffer read on seek:    %u\n",
00129                     bufstats.mBufferReadUponSeek);
00130             fprintf(tfp, "buffer unread on seek:  %u\n",
00131                     bufstats.mBufferUnreadUponSeek);
00132             fprintf(tfp, "bytes read from buffer: %u\n",
00133                     bufstats.mBytesReadFromBuffer);
00134             for (PRUint32 i = 0; i < bufstats.mBigSeekIndex; i++) {
00135                 fprintf(tfp, "bigseek[%u] = {old: %u, new: %u}\n",
00136                         i,
00137                         bufstats.mBigSeek[i].mOldOffset,
00138                         bufstats.mBigSeek[i].mNewOffset);
00139             }
00140         }
00141     }
00142 #endif
00143     return NS_OK;
00144 }
00145 
00146 NS_IMETHODIMP
00147 nsBufferedStream::Seek(PRInt32 whence, PRInt64 offset)
00148 {
00149     if (mStream == nsnull)
00150         return NS_BASE_STREAM_CLOSED;
00151     
00152     // If the underlying stream isn't a random access store, then fail early.
00153     // We could possibly succeed for the case where the seek position denotes
00154     // something that happens to be read into the buffer, but that would make
00155     // the failure data-dependent.
00156     nsresult rv;
00157     nsCOMPtr<nsISeekableStream> ras = do_QueryInterface(mStream, &rv);
00158     if (NS_FAILED(rv)) return rv;
00159 
00160     nsInt64 absPos;
00161     switch (whence) {
00162       case nsISeekableStream::NS_SEEK_SET:
00163         absPos = offset;
00164         break;
00165       case nsISeekableStream::NS_SEEK_CUR:
00166         absPos = mBufferStartOffset;
00167         absPos += mCursor;
00168         absPos += offset;
00169         break;
00170       case nsISeekableStream::NS_SEEK_END:
00171         absPos = -1;
00172         break;
00173       default:
00174         NS_NOTREACHED("bogus seek whence parameter");
00175         return NS_ERROR_UNEXPECTED;
00176     }
00177 
00178     // Let mCursor point into the existing buffer if the new position is
00179     // between the current cursor and the mFillPoint "fencepost" -- the
00180     // client may never get around to a Read or Write after this Seek.
00181     // Read and Write worry about flushing and filling in that event.
00182     PRUint32 offsetInBuffer = PRUint32(absPos - mBufferStartOffset);
00183     if (offsetInBuffer <= mFillPoint) {
00184         METER(bufstats.mSeeksWithinBuffer++);
00185         mCursor = offsetInBuffer;
00186         return NS_OK;
00187     }
00188 
00189     METER(bufstats.mSeeksOutsideBuffer++);
00190     METER(bufstats.mBufferReadUponSeek += mCursor);
00191     METER(bufstats.mBufferUnreadUponSeek += mFillPoint - mCursor);
00192     rv = Flush();
00193     if (NS_FAILED(rv)) return rv;
00194 
00195     rv = ras->Seek(whence, offset);
00196     if (NS_FAILED(rv)) return rv;
00197 
00198     METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS)
00199               bufstats.mBigSeek[bufstats.mBigSeekIndex].mOldOffset =
00200                   mBufferStartOffset + nsInt64(mCursor));
00201     const nsInt64 minus1 = -1;
00202     if (absPos == minus1) {
00203         // then we had the SEEK_END case, above
00204         PRInt64 tellPos;
00205         rv = ras->Tell(&tellPos);
00206         mBufferStartOffset = tellPos;
00207         if (NS_FAILED(rv)) return rv;
00208     }
00209     else {
00210         mBufferStartOffset = absPos;
00211     }
00212     METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS)
00213               bufstats.mBigSeek[bufstats.mBigSeekIndex++].mNewOffset =
00214                   mBufferStartOffset);
00215 
00216     mFillPoint = mCursor = 0;
00217     return Fill();
00218 }
00219 
00220 NS_IMETHODIMP
00221 nsBufferedStream::Tell(PRInt64 *result)
00222 {
00223     if (mStream == nsnull)
00224         return NS_BASE_STREAM_CLOSED;
00225     
00226     nsInt64 result64 = mBufferStartOffset;
00227     result64 += mCursor;
00228     *result = result64;
00229     return NS_OK;
00230 }
00231 
00232 NS_IMETHODIMP
00233 nsBufferedStream::SetEOF()
00234 {
00235     if (mStream == nsnull)
00236         return NS_BASE_STREAM_CLOSED;
00237     
00238     nsresult rv;
00239     nsCOMPtr<nsISeekableStream> ras = do_QueryInterface(mStream, &rv);
00240     if (NS_FAILED(rv)) return rv;
00241 
00242     return ras->SetEOF();
00243 }
00244 
00246 // nsBufferedInputStream
00247 
00248 NS_IMPL_ISUPPORTS_INHERITED3(nsBufferedInputStream, 
00249                              nsBufferedStream,
00250                              nsIInputStream,
00251                              nsIBufferedInputStream,
00252                              nsIStreamBufferAccess)
00253 
00254 NS_METHOD
00255 nsBufferedInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
00256 {
00257     NS_ENSURE_NO_AGGREGATION(aOuter);
00258 
00259     nsBufferedInputStream* stream = new nsBufferedInputStream();
00260     if (stream == nsnull)
00261         return NS_ERROR_OUT_OF_MEMORY;
00262     NS_ADDREF(stream);
00263     nsresult rv = stream->QueryInterface(aIID, aResult);
00264     NS_RELEASE(stream);
00265     return rv;
00266 }
00267 
00268 NS_IMETHODIMP
00269 nsBufferedInputStream::Init(nsIInputStream* stream, PRUint32 bufferSize)
00270 {
00271     return nsBufferedStream::Init(stream, bufferSize);
00272 }
00273 
00274 NS_IMETHODIMP
00275 nsBufferedInputStream::Close()
00276 {
00277     nsresult rv1 = NS_OK, rv2;
00278     if (mStream) {
00279         rv1 = Source()->Close();
00280         NS_RELEASE(mStream);
00281     }
00282     rv2 = nsBufferedStream::Close();
00283     if (NS_FAILED(rv1)) return rv1;
00284     return rv2;
00285 }
00286 
00287 NS_IMETHODIMP
00288 nsBufferedInputStream::Available(PRUint32 *result)
00289 {
00290     nsresult rv = NS_OK;
00291     *result = 0;
00292     if (mStream) {
00293         rv = Source()->Available(result);
00294     }
00295     *result += (mFillPoint - mCursor);
00296     return rv;
00297 }
00298 
00299 NS_IMETHODIMP
00300 nsBufferedInputStream::Read(char * buf, PRUint32 count, PRUint32 *result)
00301 {
00302     nsresult rv;
00303     if (mBufferDisabled) {
00304         NS_ENSURE_TRUE(mStream, NS_BASE_STREAM_CLOSED);
00305         rv = Source()->Read(buf, count, result);
00306         if (NS_SUCCEEDED(rv))
00307             mBufferStartOffset += *result;  // so nsBufferedStream::Tell works
00308         return rv;
00309     }
00310 
00311     rv = NS_OK;
00312     PRUint32 read = 0;
00313     while (count > 0) {
00314         PRUint32 amt = PR_MIN(count, mFillPoint - mCursor);
00315         if (amt > 0) {
00316             memcpy(buf + read, mBuffer + mCursor, amt);
00317             read += amt;
00318             count -= amt;
00319             mCursor += amt;
00320         }
00321         else {
00322             rv = Fill();
00323             if (NS_FAILED(rv) || mFillPoint == mCursor)
00324                 break;
00325         }
00326     }
00327     METER(if (read > 0) bufstats.mBytesReadFromBuffer += read);
00328     *result = read;
00329     return (read > 0) ? NS_OK : rv;
00330 }
00331 
00332 NS_IMETHODIMP
00333 nsBufferedInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 count, PRUint32 *result)
00334 {
00335     nsresult rv = NS_OK;
00336     *result = 0;
00337     while (count > 0) {
00338         PRUint32 amt = PR_MIN(count, mFillPoint - mCursor);
00339         if (amt > 0) {
00340             PRUint32 read = 0;
00341             rv = writer(this, closure, mBuffer + mCursor, mCursor, amt, &read);
00342             if (NS_FAILED(rv)) {
00343                 // errors returned from the writer end here!
00344                 rv = NS_OK;
00345                 break;
00346             }
00347             *result += read;
00348             count -= read;
00349             mCursor += read;
00350         }
00351         else {
00352             rv = Fill();
00353             if (NS_FAILED(rv) || mFillPoint == mCursor)
00354                 break;
00355         }
00356     }
00357     return (*result > 0) ? NS_OK : rv;
00358 }
00359 
00360 NS_IMETHODIMP
00361 nsBufferedInputStream::IsNonBlocking(PRBool *aNonBlocking)
00362 {
00363     if (mStream)
00364         return Source()->IsNonBlocking(aNonBlocking);
00365     return NS_ERROR_NOT_INITIALIZED;
00366 }
00367 
00368 NS_IMETHODIMP
00369 nsBufferedInputStream::Fill()
00370 {
00371     if (mBufferDisabled)
00372         return NS_OK;
00373     NS_ENSURE_TRUE(mStream, NS_BASE_STREAM_CLOSED);
00374 
00375     nsresult rv;
00376     PRInt32 rem = PRInt32(mFillPoint - mCursor);
00377     if (rem > 0) {
00378         // slide the remainder down to the start of the buffer
00379         // |<------------->|<--rem-->|<--->|
00380         // b               c         f     s
00381         memcpy(mBuffer, mBuffer + mCursor, rem);
00382     }
00383     mBufferStartOffset += mCursor;
00384     mFillPoint = rem;
00385     mCursor = 0;
00386 
00387     PRUint32 amt;
00388     rv = Source()->Read(mBuffer + mFillPoint, mBufferSize - mFillPoint, &amt);
00389     if (NS_FAILED(rv)) return rv;
00390 
00391     mFillPoint += amt;
00392     return NS_OK;
00393 }
00394 
00395 NS_IMETHODIMP_(char*)
00396 nsBufferedInputStream::GetBuffer(PRUint32 aLength, PRUint32 aAlignMask)
00397 {
00398     NS_ASSERTION(mGetBufferCount == 0, "nested GetBuffer!");
00399     if (mGetBufferCount != 0)
00400         return nsnull;
00401 
00402     if (mBufferDisabled)
00403         return nsnull;
00404 
00405     char* buf = mBuffer + mCursor;
00406     PRUint32 rem = mFillPoint - mCursor;
00407     if (rem == 0) {
00408         if (NS_FAILED(Fill()))
00409             return nsnull;
00410         buf = mBuffer + mCursor;
00411         rem = mFillPoint - mCursor;
00412     }
00413 
00414     PRUint32 mod = (NS_PTR_TO_INT32(buf) & aAlignMask);
00415     if (mod) {
00416         PRUint32 pad = aAlignMask + 1 - mod;
00417         if (pad > rem)
00418             return nsnull;
00419 
00420         memset(buf, 0, pad);
00421         mCursor += pad;
00422         buf += pad;
00423         rem -= pad;
00424     }
00425 
00426     if (aLength > rem)
00427         return nsnull;
00428     mGetBufferCount++;
00429     return buf;
00430 }
00431 
00432 NS_IMETHODIMP_(void)
00433 nsBufferedInputStream::PutBuffer(char* aBuffer, PRUint32 aLength)
00434 {
00435     NS_ASSERTION(mGetBufferCount == 1, "stray PutBuffer!");
00436     if (--mGetBufferCount != 0)
00437         return;
00438 
00439     NS_ASSERTION(mCursor + aLength <= mFillPoint, "PutBuffer botch");
00440     mCursor += aLength;
00441 }
00442 
00443 NS_IMETHODIMP
00444 nsBufferedInputStream::DisableBuffering()
00445 {
00446     NS_ASSERTION(!mBufferDisabled, "redundant call to DisableBuffering!");
00447     NS_ASSERTION(mGetBufferCount == 0,
00448                  "DisableBuffer call between GetBuffer and PutBuffer!");
00449     if (mGetBufferCount != 0)
00450         return NS_ERROR_UNEXPECTED;
00451 
00452     // Empty the buffer so nsBufferedStream::Tell works.
00453     mBufferStartOffset += mCursor;
00454     mFillPoint = mCursor = 0;
00455     mBufferDisabled = PR_TRUE;
00456     return NS_OK;
00457 }
00458 
00459 NS_IMETHODIMP
00460 nsBufferedInputStream::EnableBuffering()
00461 {
00462     NS_ASSERTION(mBufferDisabled, "gratuitous call to EnableBuffering!");
00463     mBufferDisabled = PR_FALSE;
00464     return NS_OK;
00465 }
00466 
00467 NS_IMETHODIMP
00468 nsBufferedInputStream::GetUnbufferedStream(nsISupports* *aStream)
00469 {
00470     // Empty the buffer so subsequent i/o trumps any buffered data.
00471     mBufferStartOffset += mCursor;
00472     mFillPoint = mCursor = 0;
00473 
00474     *aStream = mStream;
00475     NS_IF_ADDREF(*aStream);
00476     return NS_OK;
00477 }
00478 
00480 // nsBufferedOutputStream
00481 
00482 NS_IMPL_ADDREF_INHERITED(nsBufferedOutputStream, nsBufferedStream)
00483 NS_IMPL_RELEASE_INHERITED(nsBufferedOutputStream, nsBufferedStream)
00484 // This QI uses NS_INTERFACE_MAP_ENTRY_CONDITIONAL to check for
00485 // non-nullness of mSafeStream.
00486 NS_INTERFACE_MAP_BEGIN(nsBufferedOutputStream)
00487     NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
00488     NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISafeOutputStream, mSafeStream)
00489     NS_INTERFACE_MAP_ENTRY(nsIBufferedOutputStream)
00490     NS_INTERFACE_MAP_ENTRY(nsIStreamBufferAccess)
00491 NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream)
00492 
00493 NS_METHOD
00494 nsBufferedOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
00495 {
00496     NS_ENSURE_NO_AGGREGATION(aOuter);
00497 
00498     nsBufferedOutputStream* stream = new nsBufferedOutputStream();
00499     if (stream == nsnull)
00500         return NS_ERROR_OUT_OF_MEMORY;
00501     NS_ADDREF(stream);
00502     nsresult rv = stream->QueryInterface(aIID, aResult);
00503     NS_RELEASE(stream);
00504     return rv;
00505 }
00506 
00507 NS_IMETHODIMP
00508 nsBufferedOutputStream::Init(nsIOutputStream* stream, PRUint32 bufferSize)
00509 {
00510     // QI stream to an nsISafeOutputStream, to see if we should support it
00511     mSafeStream = do_QueryInterface(stream);
00512 
00513     return nsBufferedStream::Init(stream, bufferSize);
00514 }
00515 
00516 NS_IMETHODIMP
00517 nsBufferedOutputStream::Close()
00518 {
00519     nsresult rv1, rv2 = NS_OK, rv3;
00520     rv1 = Flush();
00521     // If we fail to Flush all the data, then we close anyway and drop the
00522     // remaining data in the buffer. We do this because it's what Unix does
00523     // for fclose and close. However, we report the error from Flush anyway.
00524     if (mStream) {
00525         rv2 = Sink()->Close();
00526         NS_RELEASE(mStream);
00527     }
00528     rv3 = nsBufferedStream::Close();
00529     if (NS_FAILED(rv1)) return rv1;
00530     if (NS_FAILED(rv2)) return rv2;
00531     return rv3;
00532 }
00533 
00534 NS_IMETHODIMP
00535 nsBufferedOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *result)
00536 {
00537     nsresult rv = NS_OK;
00538     PRUint32 written = 0;
00539     while (count > 0) {
00540         PRUint32 amt = PR_MIN(count, mBufferSize - mCursor);
00541         if (amt > 0) {
00542             memcpy(mBuffer + mCursor, buf + written, amt);
00543             written += amt;
00544             count -= amt;
00545             mCursor += amt;
00546             if (mFillPoint < mCursor)
00547                 mFillPoint = mCursor;
00548         }
00549         else {
00550             NS_ASSERTION(mFillPoint, "iloop in nsBufferedOutputStream::Write!");
00551             rv = Flush();
00552             if (NS_FAILED(rv)) break;
00553         }
00554     }
00555     *result = written;
00556     return (written > 0) ? NS_OK : rv;
00557 }
00558 
00559 NS_IMETHODIMP
00560 nsBufferedOutputStream::Flush()
00561 {
00562     nsresult rv;
00563     PRUint32 amt;
00564     if (!mStream) {
00565         // Stream already cancelled/flushed; probably because of error.
00566         return NS_OK;
00567     }
00568     rv = Sink()->Write(mBuffer, mFillPoint, &amt);
00569     if (NS_FAILED(rv)) return rv;
00570     mBufferStartOffset += amt;
00571     if (amt == mFillPoint) {
00572         mFillPoint = mCursor = 0;
00573         return NS_OK;   // flushed everything
00574     }
00575 
00576     // slide the remainder down to the start of the buffer
00577     // |<-------------->|<---|----->|
00578     // b                a    c      s
00579     PRUint32 rem = mFillPoint - amt;
00580     memcpy(mBuffer, mBuffer + amt, rem);
00581     mFillPoint = mCursor = rem;
00582     return NS_ERROR_FAILURE;        // didn't flush all
00583 }
00584 
00585 // nsISafeOutputStream
00586 NS_IMETHODIMP
00587 nsBufferedOutputStream::Finish()
00588 {
00589     // flush the stream, to write out any buffered data...
00590     nsresult rv = nsBufferedOutputStream::Flush();
00591     if (NS_FAILED(rv))
00592         NS_WARNING("failed to flush buffered data! possible dataloss");
00593 
00594     // ... and finish the underlying stream...
00595     if (NS_SUCCEEDED(rv))
00596         rv = mSafeStream->Finish();
00597     else
00598         Sink()->Close();
00599 
00600     // ... and close the buffered stream, so any further attempts to flush/close
00601     // the buffered stream won't cause errors.
00602     nsBufferedStream::Close();
00603 
00604     return rv;
00605 }
00606 
00607 static NS_METHOD
00608 nsReadFromInputStream(nsIOutputStream* outStr,
00609                       void* closure,
00610                       char* toRawSegment, 
00611                       PRUint32 offset,
00612                       PRUint32 count,
00613                       PRUint32 *readCount)
00614 {
00615     nsIInputStream* fromStream = (nsIInputStream*)closure;
00616     return fromStream->Read(toRawSegment, count, readCount);
00617 }
00618 
00619 NS_IMETHODIMP
00620 nsBufferedOutputStream::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
00621 {
00622     return WriteSegments(nsReadFromInputStream, inStr, count, _retval);
00623 }
00624 
00625 NS_IMETHODIMP
00626 nsBufferedOutputStream::WriteSegments(nsReadSegmentFun reader, void * closure, PRUint32 count, PRUint32 *_retval)
00627 {
00628     *_retval = 0;
00629     nsresult rv;
00630     while (count > 0) {
00631         PRUint32 left = PR_MIN(count, mBufferSize - mCursor);
00632         if (left == 0) {
00633             rv = Flush();
00634             if (NS_FAILED(rv))
00635               return rv;
00636 
00637             continue;
00638         }
00639 
00640         PRUint32 read = 0;
00641         rv = reader(this, closure, mBuffer + mCursor, *_retval, left, &read);
00642 
00643         if (NS_FAILED(rv)) // If we have written some data, return ok
00644             return (*_retval > 0) ? NS_OK : rv;
00645         mCursor += read;
00646         *_retval += read;
00647         count -= read;
00648         mFillPoint = PR_MAX(mFillPoint, mCursor);
00649     }
00650     return NS_OK;
00651 }
00652 
00653 NS_IMETHODIMP
00654 nsBufferedOutputStream::IsNonBlocking(PRBool *aNonBlocking)
00655 {
00656     if (mStream)
00657         return Sink()->IsNonBlocking(aNonBlocking);
00658     return NS_ERROR_NOT_INITIALIZED;
00659 }
00660 
00661 NS_IMETHODIMP_(char*)
00662 nsBufferedOutputStream::GetBuffer(PRUint32 aLength, PRUint32 aAlignMask)
00663 {
00664     NS_ASSERTION(mGetBufferCount == 0, "nested GetBuffer!");
00665     if (mGetBufferCount != 0)
00666         return nsnull;
00667 
00668     if (mBufferDisabled)
00669         return nsnull;
00670 
00671     char* buf = mBuffer + mCursor;
00672     PRUint32 rem = mBufferSize - mCursor;
00673     if (rem == 0) {
00674         if (NS_FAILED(Flush()))
00675             return nsnull;
00676         buf = mBuffer + mCursor;
00677         rem = mBufferSize - mCursor;
00678     }
00679 
00680     PRUint32 mod = (NS_PTR_TO_INT32(buf) & aAlignMask);
00681     if (mod) {
00682         PRUint32 pad = aAlignMask + 1 - mod;
00683         if (pad > rem)
00684             return nsnull;
00685 
00686         memset(buf, 0, pad);
00687         mCursor += pad;
00688         buf += pad;
00689         rem -= pad;
00690     }
00691 
00692     if (aLength > rem)
00693         return nsnull;
00694     mGetBufferCount++;
00695     return buf;
00696 }
00697 
00698 NS_IMETHODIMP_(void)
00699 nsBufferedOutputStream::PutBuffer(char* aBuffer, PRUint32 aLength)
00700 {
00701     NS_ASSERTION(mGetBufferCount == 1, "stray PutBuffer!");
00702     if (--mGetBufferCount != 0)
00703         return;
00704 
00705     NS_ASSERTION(mCursor + aLength <= mBufferSize, "PutBuffer botch");
00706     mCursor += aLength;
00707     if (mFillPoint < mCursor)
00708         mFillPoint = mCursor;
00709 }
00710 
00711 NS_IMETHODIMP
00712 nsBufferedOutputStream::DisableBuffering()
00713 {
00714     NS_ASSERTION(!mBufferDisabled, "redundant call to DisableBuffering!");
00715     NS_ASSERTION(mGetBufferCount == 0,
00716                  "DisableBuffer call between GetBuffer and PutBuffer!");
00717     if (mGetBufferCount != 0)
00718         return NS_ERROR_UNEXPECTED;
00719 
00720     // Empty the buffer so nsBufferedStream::Tell works.
00721     nsresult rv = Flush();
00722     if (NS_FAILED(rv))
00723         return rv;
00724 
00725     mBufferDisabled = PR_TRUE;
00726     return NS_OK;
00727 }
00728 
00729 NS_IMETHODIMP
00730 nsBufferedOutputStream::EnableBuffering()
00731 {
00732     NS_ASSERTION(mBufferDisabled, "gratuitous call to EnableBuffering!");
00733     mBufferDisabled = PR_FALSE;
00734     return NS_OK;
00735 }
00736 
00737 NS_IMETHODIMP
00738 nsBufferedOutputStream::GetUnbufferedStream(nsISupports* *aStream)
00739 {
00740     // Empty the buffer so subsequent i/o trumps any buffered data.
00741     if (mFillPoint) {
00742         nsresult rv = Flush();
00743         if (NS_FAILED(rv))
00744             return rv;
00745     }
00746 
00747     *aStream = mStream;
00748     NS_IF_ADDREF(*aStream);
00749     return NS_OK;
00750 }
00751