On x86 compilers without fastcall, simulate it when invoking traces and un-simulate...
[wine-gecko.git] / xpcom / io / nsMultiplexInputStream.cpp
blob8f3416d17724570e87849634a6210ce6a2186de0
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
13 * License.
15 * The Original Code is frightening to behold.
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.
22 * Contributor(s):
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 ***** */
39 /**
40 * The multiplex stream concatenates a list of input streams into a single
41 * stream.
44 #include "nsMultiplexInputStream.h"
45 #include "nsIMultiplexInputStream.h"
46 #include "nsISeekableStream.h"
47 #include "nsCOMPtr.h"
48 #include "nsCOMArray.h"
49 #include "nsInt64.h"
51 class nsMultiplexInputStream : public nsIMultiplexInputStream,
52 public nsISeekableStream
54 public:
55 nsMultiplexInputStream();
57 NS_DECL_ISUPPORTS
58 NS_DECL_NSIINPUTSTREAM
59 NS_DECL_NSIMULTIPLEXINPUTSTREAM
60 NS_DECL_NSISEEKABLESTREAM
62 private:
63 ~nsMultiplexInputStream() {}
65 struct ReadSegmentsState {
66 nsIInputStream* mThisStream;
67 PRUint32 mOffset;
68 nsWriteSegmentFun mWriter;
69 void* mClosure;
70 PRBool mDone;
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;
80 nsresult mStatus;
84 NS_IMPL_THREADSAFE_ISUPPORTS3(nsMultiplexInputStream,
85 nsIMultiplexInputStream,
86 nsIInputStream,
87 nsISeekableStream)
89 nsMultiplexInputStream::nsMultiplexInputStream()
90 : mCurrentStream(0),
91 mStartedReadingCurrent(PR_FALSE),
92 mStatus(NS_OK)
96 /* readonly attribute unsigned long count; */
97 NS_IMETHODIMP
98 nsMultiplexInputStream::GetCount(PRUint32 *aCount)
100 *aCount = mStreams.Count();
101 return NS_OK;
104 /* void appendStream (in nsIInputStream stream); */
105 NS_IMETHODIMP
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); */
112 NS_IMETHODIMP
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))
119 ++mCurrentStream;
120 return NS_OK;
123 /* void removeStream (in unsigned long index); */
124 NS_IMETHODIMP
125 nsMultiplexInputStream::RemoveStream(PRUint32 aIndex)
127 PRBool result = mStreams.RemoveObjectAt(aIndex);
128 NS_ENSURE_TRUE(result, NS_ERROR_NOT_AVAILABLE);
129 if (mCurrentStream > aIndex)
130 --mCurrentStream;
131 else if (mCurrentStream == aIndex)
132 mStartedReadingCurrent = PR_FALSE;
134 return NS_OK;
137 /* nsIInputStream getStream (in unsigned long index); */
138 NS_IMETHODIMP
139 nsMultiplexInputStream::GetStream(PRUint32 aIndex, nsIInputStream **_retval)
141 *_retval = mStreams.SafeObjectAt(aIndex);
142 NS_ENSURE_TRUE(*_retval, NS_ERROR_NOT_AVAILABLE);
144 NS_ADDREF(*_retval);
145 return NS_OK;
148 /* void close (); */
149 NS_IMETHODIMP
150 nsMultiplexInputStream::Close()
152 mStatus = NS_BASE_STREAM_CLOSED;
154 nsresult rv = NS_OK;
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
160 if (NS_FAILED(rv2))
161 rv = rv2;
163 return rv;
166 /* unsigned long available (); */
167 NS_IMETHODIMP
168 nsMultiplexInputStream::Available(PRUint32 *_retval)
170 if (NS_FAILED(mStatus))
171 return mStatus;
173 nsresult rv;
174 PRUint32 avail = 0;
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;
183 *_retval = avail;
184 return NS_OK;
187 /* [noscript] unsigned long read (in charPtr buf, in unsigned long count); */
188 NS_IMETHODIMP
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).
195 *_retval = 0;
197 if (mStatus == NS_BASE_STREAM_CLOSED)
198 return NS_OK;
199 if (NS_FAILED(mStatus))
200 return mStatus;
202 nsresult rv = NS_OK;
204 PRUint32 len = mStreams.Count();
205 while (mCurrentStream < len && aCount) {
206 PRUint32 read;
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");
213 rv = NS_OK;
214 read = 0;
216 else if (NS_FAILED(rv))
217 break;
219 if (read == 0) {
220 ++mCurrentStream;
221 mStartedReadingCurrent = PR_FALSE;
223 else {
224 NS_ASSERTION(aCount >= read, "Read more than requested");
225 *_retval += read;
226 aCount -= read;
227 aBuf += read;
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); */
237 NS_IMETHODIMP
238 nsMultiplexInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure,
239 PRUint32 aCount, PRUint32 *_retval)
241 if (mStatus == NS_BASE_STREAM_CLOSED) {
242 *_retval = 0;
243 return NS_OK;
245 if (NS_FAILED(mStatus))
246 return mStatus;
248 NS_ASSERTION(aWriter, "missing aWriter");
250 nsresult rv = NS_OK;
251 ReadSegmentsState state;
252 state.mThisStream = this;
253 state.mOffset = 0;
254 state.mWriter = aWriter;
255 state.mClosure = aClosure;
256 state.mDone = PR_FALSE;
258 PRUint32 len = mStreams.Count();
259 while (mCurrentStream < len && aCount) {
260 PRUint32 read;
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");
267 rv = NS_OK;
268 read = 0;
271 // if |aWriter| decided to stop reading segments...
272 if (state.mDone || NS_FAILED(rv))
273 break;
275 // if stream is empty, then advance to the next stream.
276 if (read == 0) {
277 ++mCurrentStream;
278 mStartedReadingCurrent = PR_FALSE;
280 else {
281 NS_ASSERTION(aCount >= read, "Read more than requested");
282 state.mOffset += read;
283 aCount -= 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;
293 NS_METHOD
294 nsMultiplexInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
295 const char* aFromRawSegment,
296 PRUint32 aToOffset, PRUint32 aCount,
297 PRUint32 *aWriteCount)
299 nsresult rv;
300 ReadSegmentsState* state = (ReadSegmentsState*)aClosure;
301 rv = (state->mWriter)(state->mThisStream,
302 state->mClosure,
303 aFromRawSegment,
304 aToOffset + state->mOffset,
305 aCount,
306 aWriteCount);
307 if (NS_FAILED(rv))
308 state->mDone = PR_TRUE;
309 return rv;
312 /* readonly attribute boolean nonBlocking; */
313 NS_IMETHODIMP
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")
323 if (*aNonBlocking)
324 return NS_OK;
326 return NS_OK;
329 /* void seek (in PRInt32 whence, in PRInt32 offset); */
330 NS_IMETHODIMP
331 nsMultiplexInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
333 if (NS_FAILED(mStatus))
334 return mStatus;
336 nsresult rv;
338 // rewinding to start is easy, and should be the most common case
339 if (aWhence == NS_SEEK_SET && aOffset == 0)
341 PRUint32 i, last;
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);
350 mCurrentStream = 0;
351 mStartedReadingCurrent = PR_FALSE;
352 return NS_OK;
355 // other Seeks not implemented yet
356 return NS_ERROR_NOT_IMPLEMENTED;
359 /* PRUint32 tell (); */
360 NS_IMETHODIMP
361 nsMultiplexInputStream::Tell(PRInt64 *_retval)
363 if (NS_FAILED(mStatus))
364 return mStatus;
366 nsresult rv;
367 nsInt64 ret64 = 0;
368 PRUint32 i, last;
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);
374 PRInt64 pos;
375 rv = stream->Tell(&pos);
376 NS_ENSURE_SUCCESS(rv, rv);
377 ret64 += pos;
379 *_retval = ret64;
381 return NS_OK;
384 /* void setEOF (); */
385 NS_IMETHODIMP
386 nsMultiplexInputStream::SetEOF()
388 return NS_ERROR_NOT_IMPLEMENTED;
391 NS_METHOD
392 nsMultiplexInputStreamConstructor(nsISupports *outer,
393 REFNSIID iid,
394 void **result)
396 *result = nsnull;
398 if (outer)
399 return NS_ERROR_NO_AGGREGATION;
401 nsMultiplexInputStream *inst;
402 NS_NEWXPCOM(inst, nsMultiplexInputStream);
403 if (!inst)
404 return NS_ERROR_OUT_OF_MEMORY;
406 NS_ADDREF(inst);
407 nsresult rv = inst->QueryInterface(iid, result);
408 NS_RELEASE(inst);
410 return rv;