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
4 : *
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/
9 : *
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
13 : * License.
14 : *
15 : * The Original Code is frightening to behold.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Jonas Sicking.
19 : * Portions created by the Initial Developer are Copyright (C) 2001
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Jonas Sicking <sicking@bigfoot.com>
24 : *
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.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : /**
40 : * The multiplex stream concatenates a list of input streams into a single
41 : * stream.
42 : */
43 :
44 : #include "IPC/IPCMessageUtils.h"
45 : #include "mozilla/net/NeckoMessageUtils.h"
46 :
47 : #include "nsMultiplexInputStream.h"
48 : #include "nsIMultiplexInputStream.h"
49 : #include "nsISeekableStream.h"
50 : #include "nsCOMPtr.h"
51 : #include "nsCOMArray.h"
52 : #include "nsIIPCSerializable.h"
53 : #include "nsIClassInfoImpl.h"
54 :
55 : class nsMultiplexInputStream : public nsIMultiplexInputStream,
56 : public nsISeekableStream,
57 : public nsIIPCSerializable
58 : {
59 : public:
60 : nsMultiplexInputStream();
61 :
62 : NS_DECL_ISUPPORTS
63 : NS_DECL_NSIINPUTSTREAM
64 : NS_DECL_NSIMULTIPLEXINPUTSTREAM
65 : NS_DECL_NSISEEKABLESTREAM
66 : NS_DECL_NSIIPCSERIALIZABLE
67 :
68 : private:
69 387 : ~nsMultiplexInputStream() {}
70 :
71 : struct ReadSegmentsState {
72 : nsIInputStream* mThisStream;
73 : PRUint32 mOffset;
74 : nsWriteSegmentFun mWriter;
75 : void* mClosure;
76 : bool mDone;
77 : };
78 :
79 : static NS_METHOD ReadSegCb(nsIInputStream* aIn, void* aClosure,
80 : const char* aFromRawSegment, PRUint32 aToOffset,
81 : PRUint32 aCount, PRUint32 *aWriteCount);
82 :
83 : nsCOMArray<nsIInputStream> mStreams;
84 : PRUint32 mCurrentStream;
85 : bool mStartedReadingCurrent;
86 : nsresult mStatus;
87 : };
88 :
89 1551 : NS_IMPL_THREADSAFE_ADDREF(nsMultiplexInputStream)
90 1938 : NS_IMPL_THREADSAFE_RELEASE(nsMultiplexInputStream)
91 :
92 : NS_IMPL_CLASSINFO(nsMultiplexInputStream, NULL, nsIClassInfo::THREADSAFE,
93 : NS_MULTIPLEXINPUTSTREAM_CID)
94 :
95 784 : NS_IMPL_QUERY_INTERFACE4_CI(nsMultiplexInputStream,
96 : nsIMultiplexInputStream,
97 : nsIInputStream,
98 : nsISeekableStream,
99 1 : nsIIPCSerializable)
100 1 : NS_IMPL_CI_INTERFACE_GETTER4(nsMultiplexInputStream,
101 : nsIMultiplexInputStream,
102 : nsIInputStream,
103 : nsISeekableStream,
104 1 : nsIIPCSerializable)
105 :
106 387 : nsMultiplexInputStream::nsMultiplexInputStream()
107 : : mCurrentStream(0),
108 : mStartedReadingCurrent(false),
109 387 : mStatus(NS_OK)
110 : {
111 387 : }
112 :
113 : /* readonly attribute unsigned long count; */
114 : NS_IMETHODIMP
115 0 : nsMultiplexInputStream::GetCount(PRUint32 *aCount)
116 : {
117 0 : *aCount = mStreams.Count();
118 0 : return NS_OK;
119 : }
120 :
121 : /* void appendStream (in nsIInputStream stream); */
122 : NS_IMETHODIMP
123 776 : nsMultiplexInputStream::AppendStream(nsIInputStream *aStream)
124 : {
125 776 : return mStreams.AppendObject(aStream) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
126 : }
127 :
128 : /* void insertStream (in nsIInputStream stream, in unsigned long index); */
129 : NS_IMETHODIMP
130 0 : nsMultiplexInputStream::InsertStream(nsIInputStream *aStream, PRUint32 aIndex)
131 : {
132 0 : bool result = mStreams.InsertObjectAt(aStream, aIndex);
133 0 : NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
134 0 : if (mCurrentStream > aIndex ||
135 : (mCurrentStream == aIndex && mStartedReadingCurrent))
136 0 : ++mCurrentStream;
137 0 : return NS_OK;
138 : }
139 :
140 : /* void removeStream (in unsigned long index); */
141 : NS_IMETHODIMP
142 0 : nsMultiplexInputStream::RemoveStream(PRUint32 aIndex)
143 : {
144 0 : bool result = mStreams.RemoveObjectAt(aIndex);
145 0 : NS_ENSURE_TRUE(result, NS_ERROR_NOT_AVAILABLE);
146 0 : if (mCurrentStream > aIndex)
147 0 : --mCurrentStream;
148 0 : else if (mCurrentStream == aIndex)
149 0 : mStartedReadingCurrent = false;
150 :
151 0 : return NS_OK;
152 : }
153 :
154 : /* nsIInputStream getStream (in unsigned long index); */
155 : NS_IMETHODIMP
156 0 : nsMultiplexInputStream::GetStream(PRUint32 aIndex, nsIInputStream **_retval)
157 : {
158 0 : *_retval = mStreams.SafeObjectAt(aIndex);
159 0 : NS_ENSURE_TRUE(*_retval, NS_ERROR_NOT_AVAILABLE);
160 :
161 0 : NS_ADDREF(*_retval);
162 0 : return NS_OK;
163 : }
164 :
165 : /* void close (); */
166 : NS_IMETHODIMP
167 0 : nsMultiplexInputStream::Close()
168 : {
169 0 : mStatus = NS_BASE_STREAM_CLOSED;
170 :
171 0 : nsresult rv = NS_OK;
172 :
173 0 : PRUint32 len = mStreams.Count();
174 0 : for (PRUint32 i = 0; i < len; ++i) {
175 0 : nsresult rv2 = mStreams[i]->Close();
176 : // We still want to close all streams, but we should return an error
177 0 : if (NS_FAILED(rv2))
178 0 : rv = rv2;
179 : }
180 0 : return rv;
181 : }
182 :
183 : /* unsigned long available (); */
184 : NS_IMETHODIMP
185 390 : nsMultiplexInputStream::Available(PRUint32 *_retval)
186 : {
187 390 : if (NS_FAILED(mStatus))
188 0 : return mStatus;
189 :
190 : nsresult rv;
191 390 : PRUint32 avail = 0;
192 :
193 390 : PRUint32 len = mStreams.Count();
194 1175 : for (PRUint32 i = mCurrentStream; i < len; i++) {
195 : PRUint32 streamAvail;
196 785 : rv = mStreams[i]->Available(&streamAvail);
197 785 : NS_ENSURE_SUCCESS(rv, rv);
198 785 : avail += streamAvail;
199 : }
200 390 : *_retval = avail;
201 390 : return NS_OK;
202 : }
203 :
204 : /* [noscript] unsigned long read (in charPtr buf, in unsigned long count); */
205 : NS_IMETHODIMP
206 1161 : nsMultiplexInputStream::Read(char * aBuf, PRUint32 aCount, PRUint32 *_retval)
207 : {
208 : // It is tempting to implement this method in terms of ReadSegments, but
209 : // that would prevent this class from being used with streams that only
210 : // implement Read (e.g., file streams).
211 :
212 1161 : *_retval = 0;
213 :
214 1161 : if (mStatus == NS_BASE_STREAM_CLOSED)
215 0 : return NS_OK;
216 1161 : if (NS_FAILED(mStatus))
217 0 : return mStatus;
218 :
219 1161 : nsresult rv = NS_OK;
220 :
221 1161 : PRUint32 len = mStreams.Count();
222 3873 : while (mCurrentStream < len && aCount) {
223 : PRUint32 read;
224 1551 : rv = mStreams[mCurrentStream]->Read(aBuf, aCount, &read);
225 :
226 : // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
227 : // (This is a bug in those stream implementations)
228 1551 : if (rv == NS_BASE_STREAM_CLOSED) {
229 0 : NS_NOTREACHED("Input stream's Read method returned NS_BASE_STREAM_CLOSED");
230 0 : rv = NS_OK;
231 0 : read = 0;
232 : }
233 1551 : else if (NS_FAILED(rv))
234 0 : break;
235 :
236 1551 : if (read == 0) {
237 776 : ++mCurrentStream;
238 776 : mStartedReadingCurrent = false;
239 : }
240 : else {
241 775 : NS_ASSERTION(aCount >= read, "Read more than requested");
242 775 : *_retval += read;
243 775 : aCount -= read;
244 775 : aBuf += read;
245 775 : mStartedReadingCurrent = true;
246 : }
247 : }
248 1161 : return *_retval ? NS_OK : rv;
249 : }
250 :
251 : /* [noscript] unsigned long readSegments (in nsWriteSegmentFun writer,
252 : * in voidPtr closure,
253 : * in unsigned long count); */
254 : NS_IMETHODIMP
255 0 : nsMultiplexInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure,
256 : PRUint32 aCount, PRUint32 *_retval)
257 : {
258 0 : if (mStatus == NS_BASE_STREAM_CLOSED) {
259 0 : *_retval = 0;
260 0 : return NS_OK;
261 : }
262 0 : if (NS_FAILED(mStatus))
263 0 : return mStatus;
264 :
265 0 : NS_ASSERTION(aWriter, "missing aWriter");
266 :
267 0 : nsresult rv = NS_OK;
268 : ReadSegmentsState state;
269 0 : state.mThisStream = this;
270 0 : state.mOffset = 0;
271 0 : state.mWriter = aWriter;
272 0 : state.mClosure = aClosure;
273 0 : state.mDone = false;
274 :
275 0 : PRUint32 len = mStreams.Count();
276 0 : while (mCurrentStream < len && aCount) {
277 : PRUint32 read;
278 0 : rv = mStreams[mCurrentStream]->ReadSegments(ReadSegCb, &state, aCount, &read);
279 :
280 : // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
281 : // (This is a bug in those stream implementations)
282 0 : if (rv == NS_BASE_STREAM_CLOSED) {
283 0 : NS_NOTREACHED("Input stream's Read method returned NS_BASE_STREAM_CLOSED");
284 0 : rv = NS_OK;
285 0 : read = 0;
286 : }
287 :
288 : // if |aWriter| decided to stop reading segments...
289 0 : if (state.mDone || NS_FAILED(rv))
290 0 : break;
291 :
292 : // if stream is empty, then advance to the next stream.
293 0 : if (read == 0) {
294 0 : ++mCurrentStream;
295 0 : mStartedReadingCurrent = false;
296 : }
297 : else {
298 0 : NS_ASSERTION(aCount >= read, "Read more than requested");
299 0 : state.mOffset += read;
300 0 : aCount -= read;
301 0 : mStartedReadingCurrent = true;
302 : }
303 : }
304 :
305 : // if we successfully read some data, then this call succeeded.
306 0 : *_retval = state.mOffset;
307 0 : return state.mOffset ? NS_OK : rv;
308 : }
309 :
310 : NS_METHOD
311 0 : nsMultiplexInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
312 : const char* aFromRawSegment,
313 : PRUint32 aToOffset, PRUint32 aCount,
314 : PRUint32 *aWriteCount)
315 : {
316 : nsresult rv;
317 0 : ReadSegmentsState* state = (ReadSegmentsState*)aClosure;
318 : rv = (state->mWriter)(state->mThisStream,
319 : state->mClosure,
320 : aFromRawSegment,
321 : aToOffset + state->mOffset,
322 : aCount,
323 0 : aWriteCount);
324 0 : if (NS_FAILED(rv))
325 0 : state->mDone = true;
326 0 : return rv;
327 : }
328 :
329 : /* readonly attribute boolean nonBlocking; */
330 : NS_IMETHODIMP
331 0 : nsMultiplexInputStream::IsNonBlocking(bool *aNonBlocking)
332 : {
333 0 : PRUint32 len = mStreams.Count();
334 0 : if (len == 0) {
335 : // Claim to be non-blocking, since we won't block the caller.
336 : // On the other hand we'll never return NS_BASE_STREAM_WOULD_BLOCK,
337 : // so maybe we should claim to be blocking? It probably doesn't
338 : // matter in practice.
339 0 : *aNonBlocking = true;
340 0 : return NS_OK;
341 : }
342 0 : for (PRUint32 i = 0; i < len; ++i) {
343 0 : nsresult rv = mStreams[i]->IsNonBlocking(aNonBlocking);
344 0 : NS_ENSURE_SUCCESS(rv, rv);
345 : // If one is non-blocking the entire stream becomes non-blocking
346 : // (except that we don't implement nsIAsyncInputStream, so there's
347 : // not much for the caller to do if Read returns "would block")
348 0 : if (*aNonBlocking)
349 0 : return NS_OK;
350 : }
351 0 : return NS_OK;
352 : }
353 :
354 : /* void seek (in PRInt32 whence, in PRInt32 offset); */
355 : NS_IMETHODIMP
356 0 : nsMultiplexInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
357 : {
358 0 : if (NS_FAILED(mStatus))
359 0 : return mStatus;
360 :
361 : nsresult rv;
362 :
363 : // rewinding to start is easy, and should be the most common case
364 0 : if (aWhence == NS_SEEK_SET && aOffset == 0)
365 : {
366 : PRUint32 i, last;
367 0 : last = mStartedReadingCurrent ? mCurrentStream+1 : mCurrentStream;
368 0 : for (i = 0; i < last; ++i) {
369 0 : nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStreams[i]);
370 0 : NS_ENSURE_TRUE(stream, NS_ERROR_NO_INTERFACE);
371 :
372 0 : rv = stream->Seek(NS_SEEK_SET, 0);
373 0 : NS_ENSURE_SUCCESS(rv, rv);
374 : }
375 0 : mCurrentStream = 0;
376 0 : mStartedReadingCurrent = false;
377 0 : return NS_OK;
378 : }
379 :
380 : // other Seeks not implemented yet
381 0 : return NS_ERROR_NOT_IMPLEMENTED;
382 : }
383 :
384 : /* PRUint32 tell (); */
385 : NS_IMETHODIMP
386 0 : nsMultiplexInputStream::Tell(PRInt64 *_retval)
387 : {
388 0 : if (NS_FAILED(mStatus))
389 0 : return mStatus;
390 :
391 : nsresult rv;
392 0 : PRInt64 ret64 = 0;
393 : PRUint32 i, last;
394 0 : last = mStartedReadingCurrent ? mCurrentStream+1 : mCurrentStream;
395 0 : for (i = 0; i < last; ++i) {
396 0 : nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStreams[i]);
397 0 : NS_ENSURE_TRUE(stream, NS_ERROR_NO_INTERFACE);
398 :
399 : PRInt64 pos;
400 0 : rv = stream->Tell(&pos);
401 0 : NS_ENSURE_SUCCESS(rv, rv);
402 0 : ret64 += pos;
403 : }
404 0 : *_retval = ret64;
405 :
406 0 : return NS_OK;
407 : }
408 :
409 : /* void setEOF (); */
410 : NS_IMETHODIMP
411 0 : nsMultiplexInputStream::SetEOF()
412 : {
413 0 : return NS_ERROR_NOT_IMPLEMENTED;
414 : }
415 :
416 : nsresult
417 387 : nsMultiplexInputStreamConstructor(nsISupports *outer,
418 : REFNSIID iid,
419 : void **result)
420 : {
421 387 : *result = nsnull;
422 :
423 387 : if (outer)
424 0 : return NS_ERROR_NO_AGGREGATION;
425 :
426 387 : nsMultiplexInputStream *inst = new nsMultiplexInputStream();
427 387 : if (!inst)
428 0 : return NS_ERROR_OUT_OF_MEMORY;
429 :
430 387 : NS_ADDREF(inst);
431 387 : nsresult rv = inst->QueryInterface(iid, result);
432 387 : NS_RELEASE(inst);
433 :
434 387 : return rv;
435 : }
436 :
437 : bool
438 0 : nsMultiplexInputStream::Read(const IPC::Message *aMsg, void **aIter)
439 : {
440 : using IPC::ReadParam;
441 :
442 : PRUint32 count;
443 0 : if (!ReadParam(aMsg, aIter, &count))
444 0 : return false;
445 :
446 0 : for (PRUint32 i = 0; i < count; i++) {
447 0 : IPC::InputStream inputStream;
448 0 : if (!ReadParam(aMsg, aIter, &inputStream))
449 0 : return false;
450 :
451 0 : nsCOMPtr<nsIInputStream> stream(inputStream);
452 0 : nsresult rv = AppendStream(stream);
453 0 : if (NS_FAILED(rv))
454 0 : return false;
455 : }
456 :
457 0 : if (!ReadParam(aMsg, aIter, &mCurrentStream) ||
458 0 : !ReadParam(aMsg, aIter, &mStartedReadingCurrent) ||
459 0 : !ReadParam(aMsg, aIter, &mStatus))
460 0 : return false;
461 :
462 0 : return true;
463 : }
464 :
465 : void
466 0 : nsMultiplexInputStream::Write(IPC::Message *aMsg)
467 : {
468 : using IPC::WriteParam;
469 :
470 0 : PRUint32 count = mStreams.Count();
471 0 : WriteParam(aMsg, count);
472 :
473 0 : for (PRUint32 i = 0; i < count; i++) {
474 0 : IPC::InputStream inputStream(mStreams.ObjectAt(i));
475 0 : WriteParam(aMsg, inputStream);
476 : }
477 :
478 0 : WriteParam(aMsg, mCurrentStream);
479 0 : WriteParam(aMsg, mStartedReadingCurrent);
480 0 : WriteParam(aMsg, mStatus);
481 0 : }
|