Back to index

lightning-sunbird  0.9+nobinonly
nsMultiplexInputStream.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; 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 frightening to behold.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Jonas Sicking.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Jonas Sicking <sicking@bigfoot.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00044 #include "nsMultiplexInputStream.h"
00045 #include "nsIMultiplexInputStream.h"
00046 #include "nsISeekableStream.h"
00047 #include "nsSupportsArray.h"
00048 #include "nsInt64.h"
00049 
00050 class nsMultiplexInputStream : public nsIMultiplexInputStream,
00051                                public nsISeekableStream
00052 {
00053 public:
00054     nsMultiplexInputStream();
00055 
00056     NS_DECL_ISUPPORTS
00057     NS_DECL_NSIINPUTSTREAM
00058     NS_DECL_NSIMULTIPLEXINPUTSTREAM
00059     NS_DECL_NSISEEKABLESTREAM
00060 
00061     static NS_METHOD Create(nsISupports *outer, REFNSIID iid, void **result);
00062 
00063 private:
00064     ~nsMultiplexInputStream() {}
00065 
00066 
00067     struct ReadSegmentsState {
00068         nsIInputStream* mThisStream;
00069         PRUint32 mOffset;
00070         nsWriteSegmentFun mWriter;
00071         void* mClosure;
00072         PRBool mDone;
00073     };
00074 
00075     static NS_METHOD ReadSegCb(nsIInputStream* aIn, void* aClosure,
00076                                const char* aFromRawSegment, PRUint32 aToOffset,
00077                                PRUint32 aCount, PRUint32 *aWriteCount);
00078     
00079     nsSupportsArray mStreams;
00080     PRUint32 mCurrentStream;
00081     PRBool mStartedReadingCurrent;
00082 };
00083 
00084 
00085 NS_IMPL_THREADSAFE_ISUPPORTS3(nsMultiplexInputStream,
00086                               nsIMultiplexInputStream,
00087                               nsIInputStream,
00088                               nsISeekableStream)
00089 
00090 nsMultiplexInputStream::nsMultiplexInputStream()
00091     : mCurrentStream(0),
00092       mStartedReadingCurrent(PR_FALSE)
00093 {
00094 }
00095 
00096 /* readonly attribute unsigned long count; */
00097 NS_IMETHODIMP
00098 nsMultiplexInputStream::GetCount(PRUint32 *aCount)
00099 {
00100     mStreams.Count(aCount);
00101     return NS_OK;
00102 }
00103 
00104 /* void appendStream (in nsIInputStream stream); */
00105 NS_IMETHODIMP
00106 nsMultiplexInputStream::AppendStream(nsIInputStream *aStream)
00107 {
00108     return mStreams.AppendElement(aStream);
00109 }
00110 
00111 /* void insertStream (in nsIInputStream stream, in unsigned long index); */
00112 NS_IMETHODIMP
00113 nsMultiplexInputStream::InsertStream(nsIInputStream *aStream, PRUint32 aIndex)
00114 {
00115     nsresult rv = mStreams.InsertElementAt(aStream, aIndex);
00116     NS_ENSURE_SUCCESS(rv, rv);
00117     if (mCurrentStream > aIndex ||
00118         (mCurrentStream == aIndex && mStartedReadingCurrent))
00119         ++mCurrentStream;
00120     return rv;
00121 }
00122 
00123 /* void removeStream (in unsigned long index); */
00124 NS_IMETHODIMP
00125 nsMultiplexInputStream::RemoveStream(PRUint32 aIndex)
00126 {
00127     nsresult rv = mStreams.RemoveElementAt(aIndex);
00128     NS_ENSURE_SUCCESS(rv, rv);
00129     if (mCurrentStream > aIndex)
00130         --mCurrentStream;
00131     else if (mCurrentStream == aIndex)
00132         mStartedReadingCurrent = PR_FALSE;
00133 
00134     return rv;
00135 }
00136 
00137 /* nsIInputStream getStream (in unsigned long index); */
00138 NS_IMETHODIMP
00139 nsMultiplexInputStream::GetStream(PRUint32 aIndex, nsIInputStream **_retval)
00140 {
00141     return mStreams.QueryElementAt(aIndex,
00142                                    NS_GET_IID(nsIInputStream),
00143                                    (void**)_retval);
00144 }
00145 
00146 /* void close (); */
00147 NS_IMETHODIMP
00148 nsMultiplexInputStream::Close()
00149 {
00150     PRUint32 len, i;
00151     nsresult rv = NS_OK;
00152 
00153     mStreams.Count(&len);
00154     for (i = 0; i < len; ++i) {
00155         nsCOMPtr<nsIInputStream> stream(do_QueryElementAt(&mStreams, i));
00156         nsresult rv2 = stream->Close();
00157         // We still want to close all streams, but we should return an error
00158         if (NS_FAILED(rv2))
00159             rv = rv2;
00160     }
00161     return rv;
00162 }
00163 
00164 /* unsigned long available (); */
00165 NS_IMETHODIMP
00166 nsMultiplexInputStream::Available(PRUint32 *_retval)
00167 {
00168     nsresult rv;
00169     PRUint32 i, len, avail = 0;
00170 
00171     mStreams.Count(&len);
00172     for (i = mCurrentStream; i < len; i++) {
00173         nsCOMPtr<nsIInputStream> stream(do_QueryElementAt(&mStreams, i));
00174         
00175         PRUint32 streamAvail;
00176         rv = stream->Available(&streamAvail);
00177         NS_ENSURE_SUCCESS(rv, rv);
00178         avail += streamAvail;
00179     }
00180     *_retval = avail;
00181     return NS_OK;
00182 }
00183 
00184 /* [noscript] unsigned long read (in charPtr buf, in unsigned long count); */
00185 NS_IMETHODIMP
00186 nsMultiplexInputStream::Read(char * aBuf, PRUint32 aCount, PRUint32 *_retval)
00187 {
00188     nsresult rv = NS_OK;
00189     PRUint32 len, read;
00190 
00191     *_retval = 0;
00192 
00193     mStreams.Count(&len);
00194     while (mCurrentStream < len && aCount) {
00195         nsCOMPtr<nsIInputStream> stream(do_QueryElementAt(&mStreams,
00196                                                           mCurrentStream));
00197         rv = stream->Read(aBuf, aCount, &read);
00198 
00199         // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
00200         if (rv == NS_BASE_STREAM_CLOSED) {
00201             rv = NS_OK;
00202             read = 0;
00203         }
00204         else if (NS_FAILED(rv))
00205             break;
00206 
00207         if (read == 0) {
00208             ++mCurrentStream;
00209             mStartedReadingCurrent = PR_FALSE;
00210         }
00211         else {
00212             NS_ASSERTION(aCount >= read, "Read more than requested");
00213             *_retval += read;
00214             aCount -= read;
00215             aBuf += read;
00216             mStartedReadingCurrent = PR_TRUE;
00217         }
00218     }
00219     return *_retval ? NS_OK : rv;
00220 }
00221 
00222 /* [noscript] unsigned long readSegments (in nsWriteSegmentFun writer,
00223  *                                        in voidPtr closure,
00224  *                                        in unsigned long count); */
00225 NS_IMETHODIMP
00226 nsMultiplexInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure,
00227                                      PRUint32 aCount, PRUint32 *_retval)
00228 {
00229     NS_ASSERTION(aWriter, "missing aWriter");
00230 
00231     nsresult rv = NS_OK;
00232     ReadSegmentsState state;
00233     state.mThisStream = this;
00234     state.mOffset = 0;
00235     state.mWriter = aWriter;
00236     state.mClosure = aClosure;
00237     state.mDone = PR_FALSE;
00238     
00239     PRUint32 len;
00240     mStreams.Count(&len);
00241     while (mCurrentStream < len && aCount) {
00242         nsCOMPtr<nsIInputStream> stream(do_QueryElementAt(&mStreams,
00243                                                           mCurrentStream));
00244         PRUint32 read;
00245         rv = stream->ReadSegments(ReadSegCb, &state, aCount, &read);
00246 
00247         // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
00248         if (rv == NS_BASE_STREAM_CLOSED) {
00249             rv = NS_OK;
00250             read = 0;
00251         }
00252 
00253         // if |aWriter| decided to stop reading segments...
00254         if (state.mDone || NS_FAILED(rv))
00255             break;
00256 
00257         // if stream is empty, then advance to the next stream.
00258         if (read == 0) {
00259             ++mCurrentStream;
00260             mStartedReadingCurrent = PR_FALSE;
00261         }
00262         else {
00263             NS_ASSERTION(aCount >= read, "Read more than requested");
00264             state.mOffset += read;
00265             aCount -= read;
00266             mStartedReadingCurrent = PR_TRUE;
00267         }
00268     }
00269 
00270     // if we successfully read some data, then this call succeeded.
00271     *_retval = state.mOffset;
00272     return state.mOffset ? NS_OK : rv;
00273 }
00274 
00275 NS_METHOD
00276 nsMultiplexInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
00277                                   const char* aFromRawSegment,
00278                                   PRUint32 aToOffset, PRUint32 aCount,
00279                                   PRUint32 *aWriteCount)
00280 {
00281     nsresult rv;
00282     ReadSegmentsState* state = (ReadSegmentsState*)aClosure;
00283     rv = (state->mWriter)(state->mThisStream,
00284                           state->mClosure,
00285                           aFromRawSegment,
00286                           aToOffset + state->mOffset,
00287                           aCount,
00288                           aWriteCount);
00289     if (NS_FAILED(rv))
00290         state->mDone = PR_TRUE;
00291     return rv;
00292 }
00293 
00294 /* readonly attribute boolean nonBlocking; */
00295 NS_IMETHODIMP
00296 nsMultiplexInputStream::IsNonBlocking(PRBool *aNonBlocking)
00297 {
00298     nsresult rv;
00299     PRUint32 i, len;
00300 
00301     mStreams.Count(&len);
00302     for (i = 0; i < len; ++i) {
00303         nsCOMPtr<nsIInputStream> stream(do_QueryElementAt(&mStreams, i));
00304         rv = stream->IsNonBlocking(aNonBlocking);
00305         NS_ENSURE_SUCCESS(rv, rv);
00306         // If one is non-blocking the entire stream becomes non-blocking
00307         if (*aNonBlocking)
00308             return NS_OK;
00309     }
00310     return NS_OK;
00311 }
00312 
00313 /* void seek (in PRInt32 whence, in PRInt32 offset); */
00314 NS_IMETHODIMP
00315 nsMultiplexInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
00316 {
00317     nsresult rv;
00318 
00319     // rewinding to start is easy, and should be the most common case
00320     if (aWhence == NS_SEEK_SET && aOffset == 0)
00321     {
00322         PRUint32 i, last;
00323         last = mStartedReadingCurrent ? mCurrentStream+1 : mCurrentStream;
00324         for (i = 0; i < last; ++i) {
00325             nsCOMPtr<nsISeekableStream> stream(do_QueryElementAt(&mStreams, i));
00326             NS_ENSURE_TRUE(stream, NS_ERROR_NO_INTERFACE);
00327 
00328             rv = stream->Seek(NS_SEEK_SET, 0);
00329             NS_ENSURE_SUCCESS(rv, rv);
00330         }
00331         mCurrentStream = 0;
00332         mStartedReadingCurrent = PR_FALSE;
00333         return NS_OK;
00334     }
00335 
00336     // other Seeks not implemented yet
00337     return NS_ERROR_NOT_IMPLEMENTED;
00338 }
00339 
00340 /* PRUint32 tell (); */
00341 NS_IMETHODIMP
00342 nsMultiplexInputStream::Tell(PRInt64 *_retval)
00343 {
00344     nsresult rv;
00345     nsInt64 ret64 = 0;
00346     PRUint32 i, last;
00347     last = mStartedReadingCurrent ? mCurrentStream+1 : mCurrentStream;
00348     for (i = 0; i < last; ++i) {
00349         nsCOMPtr<nsISeekableStream> stream(do_QueryElementAt(&mStreams, i));
00350         NS_ENSURE_TRUE(stream, NS_ERROR_NO_INTERFACE);
00351 
00352         PRInt64 pos;
00353         rv = stream->Tell(&pos);
00354         NS_ENSURE_SUCCESS(rv, rv);
00355         ret64 += pos;
00356     }
00357     *_retval =  ret64;
00358 
00359     return NS_OK;
00360 }
00361 
00362 /* void setEOF (); */
00363 NS_IMETHODIMP
00364 nsMultiplexInputStream::SetEOF()
00365 {
00366     return NS_ERROR_NOT_IMPLEMENTED;
00367 }
00368 
00369 NS_METHOD
00370 nsMultiplexInputStreamConstructor(nsISupports *outer,
00371                                   REFNSIID iid,
00372                                   void **result)
00373 {
00374     *result = nsnull;
00375 
00376     if (outer)
00377         return NS_ERROR_NO_AGGREGATION;
00378 
00379     nsMultiplexInputStream *inst;
00380     NS_NEWXPCOM(inst, nsMultiplexInputStream);
00381     if (!inst)
00382         return NS_ERROR_OUT_OF_MEMORY;
00383 
00384     NS_ADDREF(inst);
00385     nsresult rv = inst->QueryInterface(iid, result);
00386     NS_RELEASE(inst);
00387 
00388     return rv;
00389 }