1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is frightening to behold.
17 * The Initial Developer of the Original Code is
19 * Portions created by the Initial Developer are Copyright (C) 2001
20 * the Initial Developer. All Rights Reserved.
23 * Jonas Sicking <sicking@bigfoot.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
40 * The multiplex stream concatenates a list of input streams into a single
44 #include "nsMultiplexInputStream.h"
45 #include "nsIMultiplexInputStream.h"
46 #include "nsISeekableStream.h"
48 #include "nsCOMArray.h"
51 class nsMultiplexInputStream
: public nsIMultiplexInputStream
,
52 public nsISeekableStream
55 nsMultiplexInputStream();
58 NS_DECL_NSIINPUTSTREAM
59 NS_DECL_NSIMULTIPLEXINPUTSTREAM
60 NS_DECL_NSISEEKABLESTREAM
63 ~nsMultiplexInputStream() {}
65 struct ReadSegmentsState
{
66 nsIInputStream
* mThisStream
;
68 nsWriteSegmentFun mWriter
;
73 static NS_METHOD
ReadSegCb(nsIInputStream
* aIn
, void* aClosure
,
74 const char* aFromRawSegment
, PRUint32 aToOffset
,
75 PRUint32 aCount
, PRUint32
*aWriteCount
);
77 nsCOMArray
<nsIInputStream
> mStreams
;
78 PRUint32 mCurrentStream
;
79 PRBool mStartedReadingCurrent
;
84 NS_IMPL_THREADSAFE_ISUPPORTS3(nsMultiplexInputStream
,
85 nsIMultiplexInputStream
,
89 nsMultiplexInputStream::nsMultiplexInputStream()
91 mStartedReadingCurrent(PR_FALSE
),
96 /* readonly attribute unsigned long count; */
98 nsMultiplexInputStream::GetCount(PRUint32
*aCount
)
100 *aCount
= mStreams
.Count();
104 /* void appendStream (in nsIInputStream stream); */
106 nsMultiplexInputStream::AppendStream(nsIInputStream
*aStream
)
108 return mStreams
.AppendObject(aStream
) ? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
111 /* void insertStream (in nsIInputStream stream, in unsigned long index); */
113 nsMultiplexInputStream::InsertStream(nsIInputStream
*aStream
, PRUint32 aIndex
)
115 PRBool result
= mStreams
.InsertObjectAt(aStream
, aIndex
);
116 NS_ENSURE_TRUE(result
, NS_ERROR_OUT_OF_MEMORY
);
117 if (mCurrentStream
> aIndex
||
118 (mCurrentStream
== aIndex
&& mStartedReadingCurrent
))
123 /* void removeStream (in unsigned long index); */
125 nsMultiplexInputStream::RemoveStream(PRUint32 aIndex
)
127 PRBool result
= mStreams
.RemoveObjectAt(aIndex
);
128 NS_ENSURE_TRUE(result
, NS_ERROR_NOT_AVAILABLE
);
129 if (mCurrentStream
> aIndex
)
131 else if (mCurrentStream
== aIndex
)
132 mStartedReadingCurrent
= PR_FALSE
;
137 /* nsIInputStream getStream (in unsigned long index); */
139 nsMultiplexInputStream::GetStream(PRUint32 aIndex
, nsIInputStream
**_retval
)
141 *_retval
= mStreams
.SafeObjectAt(aIndex
);
142 NS_ENSURE_TRUE(*_retval
, NS_ERROR_NOT_AVAILABLE
);
150 nsMultiplexInputStream::Close()
152 mStatus
= NS_BASE_STREAM_CLOSED
;
156 PRUint32 len
= mStreams
.Count();
157 for (PRUint32 i
= 0; i
< len
; ++i
) {
158 nsresult rv2
= mStreams
[i
]->Close();
159 // We still want to close all streams, but we should return an error
166 /* unsigned long available (); */
168 nsMultiplexInputStream::Available(PRUint32
*_retval
)
170 if (NS_FAILED(mStatus
))
176 PRUint32 len
= mStreams
.Count();
177 for (PRUint32 i
= mCurrentStream
; i
< len
; i
++) {
178 PRUint32 streamAvail
;
179 rv
= mStreams
[i
]->Available(&streamAvail
);
180 NS_ENSURE_SUCCESS(rv
, rv
);
181 avail
+= streamAvail
;
187 /* [noscript] unsigned long read (in charPtr buf, in unsigned long count); */
189 nsMultiplexInputStream::Read(char * aBuf
, PRUint32 aCount
, PRUint32
*_retval
)
191 // It is tempting to implement this method in terms of ReadSegments, but
192 // that would prevent this class from being used with streams that only
193 // implement Read (e.g., file streams).
197 if (mStatus
== NS_BASE_STREAM_CLOSED
)
199 if (NS_FAILED(mStatus
))
204 PRUint32 len
= mStreams
.Count();
205 while (mCurrentStream
< len
&& aCount
) {
207 rv
= mStreams
[mCurrentStream
]->Read(aBuf
, aCount
, &read
);
209 // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
210 // (This is a bug in those stream implementations)
211 if (rv
== NS_BASE_STREAM_CLOSED
) {
212 NS_NOTREACHED("Input stream's Read method returned NS_BASE_STREAM_CLOSED");
216 else if (NS_FAILED(rv
))
221 mStartedReadingCurrent
= PR_FALSE
;
224 NS_ASSERTION(aCount
>= read
, "Read more than requested");
228 mStartedReadingCurrent
= PR_TRUE
;
231 return *_retval
? NS_OK
: rv
;
234 /* [noscript] unsigned long readSegments (in nsWriteSegmentFun writer,
235 * in voidPtr closure,
236 * in unsigned long count); */
238 nsMultiplexInputStream::ReadSegments(nsWriteSegmentFun aWriter
, void *aClosure
,
239 PRUint32 aCount
, PRUint32
*_retval
)
241 if (mStatus
== NS_BASE_STREAM_CLOSED
) {
245 if (NS_FAILED(mStatus
))
248 NS_ASSERTION(aWriter
, "missing aWriter");
251 ReadSegmentsState state
;
252 state
.mThisStream
= this;
254 state
.mWriter
= aWriter
;
255 state
.mClosure
= aClosure
;
256 state
.mDone
= PR_FALSE
;
258 PRUint32 len
= mStreams
.Count();
259 while (mCurrentStream
< len
&& aCount
) {
261 rv
= mStreams
[mCurrentStream
]->ReadSegments(ReadSegCb
, &state
, aCount
, &read
);
263 // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
264 // (This is a bug in those stream implementations)
265 if (rv
== NS_BASE_STREAM_CLOSED
) {
266 NS_NOTREACHED("Input stream's Read method returned NS_BASE_STREAM_CLOSED");
271 // if |aWriter| decided to stop reading segments...
272 if (state
.mDone
|| NS_FAILED(rv
))
275 // if stream is empty, then advance to the next stream.
278 mStartedReadingCurrent
= PR_FALSE
;
281 NS_ASSERTION(aCount
>= read
, "Read more than requested");
282 state
.mOffset
+= read
;
284 mStartedReadingCurrent
= PR_TRUE
;
288 // if we successfully read some data, then this call succeeded.
289 *_retval
= state
.mOffset
;
290 return state
.mOffset
? NS_OK
: rv
;
294 nsMultiplexInputStream::ReadSegCb(nsIInputStream
* aIn
, void* aClosure
,
295 const char* aFromRawSegment
,
296 PRUint32 aToOffset
, PRUint32 aCount
,
297 PRUint32
*aWriteCount
)
300 ReadSegmentsState
* state
= (ReadSegmentsState
*)aClosure
;
301 rv
= (state
->mWriter
)(state
->mThisStream
,
304 aToOffset
+ state
->mOffset
,
308 state
->mDone
= PR_TRUE
;
312 /* readonly attribute boolean nonBlocking; */
314 nsMultiplexInputStream::IsNonBlocking(PRBool
*aNonBlocking
)
316 PRUint32 len
= mStreams
.Count();
317 for (PRUint32 i
= 0; i
< len
; ++i
) {
318 nsresult rv
= mStreams
[i
]->IsNonBlocking(aNonBlocking
);
319 NS_ENSURE_SUCCESS(rv
, rv
);
320 // If one is non-blocking the entire stream becomes non-blocking
321 // (except that we don't implement nsIAsyncInputStream, so there's
322 // not much for the caller to do if Read returns "would block")
329 /* void seek (in PRInt32 whence, in PRInt32 offset); */
331 nsMultiplexInputStream::Seek(PRInt32 aWhence
, PRInt64 aOffset
)
333 if (NS_FAILED(mStatus
))
338 // rewinding to start is easy, and should be the most common case
339 if (aWhence
== NS_SEEK_SET
&& aOffset
== 0)
342 last
= mStartedReadingCurrent
? mCurrentStream
+1 : mCurrentStream
;
343 for (i
= 0; i
< last
; ++i
) {
344 nsCOMPtr
<nsISeekableStream
> stream
= do_QueryInterface(mStreams
[i
]);
345 NS_ENSURE_TRUE(stream
, NS_ERROR_NO_INTERFACE
);
347 rv
= stream
->Seek(NS_SEEK_SET
, 0);
348 NS_ENSURE_SUCCESS(rv
, rv
);
351 mStartedReadingCurrent
= PR_FALSE
;
355 // other Seeks not implemented yet
356 return NS_ERROR_NOT_IMPLEMENTED
;
359 /* PRUint32 tell (); */
361 nsMultiplexInputStream::Tell(PRInt64
*_retval
)
363 if (NS_FAILED(mStatus
))
369 last
= mStartedReadingCurrent
? mCurrentStream
+1 : mCurrentStream
;
370 for (i
= 0; i
< last
; ++i
) {
371 nsCOMPtr
<nsISeekableStream
> stream
= do_QueryInterface(mStreams
[i
]);
372 NS_ENSURE_TRUE(stream
, NS_ERROR_NO_INTERFACE
);
375 rv
= stream
->Tell(&pos
);
376 NS_ENSURE_SUCCESS(rv
, rv
);
384 /* void setEOF (); */
386 nsMultiplexInputStream::SetEOF()
388 return NS_ERROR_NOT_IMPLEMENTED
;
392 nsMultiplexInputStreamConstructor(nsISupports
*outer
,
399 return NS_ERROR_NO_AGGREGATION
;
401 nsMultiplexInputStream
*inst
;
402 NS_NEWXPCOM(inst
, nsMultiplexInputStream
);
404 return NS_ERROR_OUT_OF_MEMORY
;
407 nsresult rv
= inst
->QueryInterface(iid
, result
);