|
1 /* vim:set ts=4 sw=4 sts=4 et cin: */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "mozilla/Mutex.h" |
|
7 #include "mozilla/Attributes.h" |
|
8 #include "nsStreamUtils.h" |
|
9 #include "nsAutoPtr.h" |
|
10 #include "nsCOMPtr.h" |
|
11 #include "nsIPipe.h" |
|
12 #include "nsIEventTarget.h" |
|
13 #include "nsIRunnable.h" |
|
14 #include "nsISafeOutputStream.h" |
|
15 #include "nsString.h" |
|
16 #include "nsIAsyncInputStream.h" |
|
17 #include "nsIAsyncOutputStream.h" |
|
18 #include "nsIBufferedStreams.h" |
|
19 |
|
20 using namespace mozilla; |
|
21 |
|
22 //----------------------------------------------------------------------------- |
|
23 |
|
24 class nsInputStreamReadyEvent MOZ_FINAL : public nsIRunnable |
|
25 , public nsIInputStreamCallback |
|
26 { |
|
27 public: |
|
28 NS_DECL_THREADSAFE_ISUPPORTS |
|
29 |
|
30 nsInputStreamReadyEvent(nsIInputStreamCallback *callback, |
|
31 nsIEventTarget *target) |
|
32 : mCallback(callback) |
|
33 , mTarget(target) |
|
34 { |
|
35 } |
|
36 |
|
37 private: |
|
38 ~nsInputStreamReadyEvent() |
|
39 { |
|
40 if (!mCallback) |
|
41 return; |
|
42 // |
|
43 // whoa!! looks like we never posted this event. take care to |
|
44 // release mCallback on the correct thread. if mTarget lives on the |
|
45 // calling thread, then we are ok. otherwise, we have to try to |
|
46 // proxy the Release over the right thread. if that thread is dead, |
|
47 // then there's nothing we can do... better to leak than crash. |
|
48 // |
|
49 bool val; |
|
50 nsresult rv = mTarget->IsOnCurrentThread(&val); |
|
51 if (NS_FAILED(rv) || !val) { |
|
52 nsCOMPtr<nsIInputStreamCallback> event = |
|
53 NS_NewInputStreamReadyEvent(mCallback, mTarget); |
|
54 mCallback = nullptr; |
|
55 if (event) { |
|
56 rv = event->OnInputStreamReady(nullptr); |
|
57 if (NS_FAILED(rv)) { |
|
58 NS_NOTREACHED("leaking stream event"); |
|
59 nsISupports *sup = event; |
|
60 NS_ADDREF(sup); |
|
61 } |
|
62 } |
|
63 } |
|
64 } |
|
65 |
|
66 public: |
|
67 NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *stream) |
|
68 { |
|
69 mStream = stream; |
|
70 |
|
71 nsresult rv = |
|
72 mTarget->Dispatch(this, NS_DISPATCH_NORMAL); |
|
73 if (NS_FAILED(rv)) { |
|
74 NS_WARNING("Dispatch failed"); |
|
75 return NS_ERROR_FAILURE; |
|
76 } |
|
77 |
|
78 return NS_OK; |
|
79 } |
|
80 |
|
81 NS_IMETHOD Run() |
|
82 { |
|
83 if (mCallback) { |
|
84 if (mStream) |
|
85 mCallback->OnInputStreamReady(mStream); |
|
86 mCallback = nullptr; |
|
87 } |
|
88 return NS_OK; |
|
89 } |
|
90 |
|
91 private: |
|
92 nsCOMPtr<nsIAsyncInputStream> mStream; |
|
93 nsCOMPtr<nsIInputStreamCallback> mCallback; |
|
94 nsCOMPtr<nsIEventTarget> mTarget; |
|
95 }; |
|
96 |
|
97 NS_IMPL_ISUPPORTS(nsInputStreamReadyEvent, nsIRunnable, |
|
98 nsIInputStreamCallback) |
|
99 |
|
100 //----------------------------------------------------------------------------- |
|
101 |
|
102 class nsOutputStreamReadyEvent MOZ_FINAL : public nsIRunnable |
|
103 , public nsIOutputStreamCallback |
|
104 { |
|
105 public: |
|
106 NS_DECL_THREADSAFE_ISUPPORTS |
|
107 |
|
108 nsOutputStreamReadyEvent(nsIOutputStreamCallback *callback, |
|
109 nsIEventTarget *target) |
|
110 : mCallback(callback) |
|
111 , mTarget(target) |
|
112 { |
|
113 } |
|
114 |
|
115 private: |
|
116 ~nsOutputStreamReadyEvent() |
|
117 { |
|
118 if (!mCallback) |
|
119 return; |
|
120 // |
|
121 // whoa!! looks like we never posted this event. take care to |
|
122 // release mCallback on the correct thread. if mTarget lives on the |
|
123 // calling thread, then we are ok. otherwise, we have to try to |
|
124 // proxy the Release over the right thread. if that thread is dead, |
|
125 // then there's nothing we can do... better to leak than crash. |
|
126 // |
|
127 bool val; |
|
128 nsresult rv = mTarget->IsOnCurrentThread(&val); |
|
129 if (NS_FAILED(rv) || !val) { |
|
130 nsCOMPtr<nsIOutputStreamCallback> event = |
|
131 NS_NewOutputStreamReadyEvent(mCallback, mTarget); |
|
132 mCallback = nullptr; |
|
133 if (event) { |
|
134 rv = event->OnOutputStreamReady(nullptr); |
|
135 if (NS_FAILED(rv)) { |
|
136 NS_NOTREACHED("leaking stream event"); |
|
137 nsISupports *sup = event; |
|
138 NS_ADDREF(sup); |
|
139 } |
|
140 } |
|
141 } |
|
142 } |
|
143 |
|
144 public: |
|
145 NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *stream) |
|
146 { |
|
147 mStream = stream; |
|
148 |
|
149 nsresult rv = |
|
150 mTarget->Dispatch(this, NS_DISPATCH_NORMAL); |
|
151 if (NS_FAILED(rv)) { |
|
152 NS_WARNING("PostEvent failed"); |
|
153 return NS_ERROR_FAILURE; |
|
154 } |
|
155 |
|
156 return NS_OK; |
|
157 } |
|
158 |
|
159 NS_IMETHOD Run() |
|
160 { |
|
161 if (mCallback) { |
|
162 if (mStream) |
|
163 mCallback->OnOutputStreamReady(mStream); |
|
164 mCallback = nullptr; |
|
165 } |
|
166 return NS_OK; |
|
167 } |
|
168 |
|
169 private: |
|
170 nsCOMPtr<nsIAsyncOutputStream> mStream; |
|
171 nsCOMPtr<nsIOutputStreamCallback> mCallback; |
|
172 nsCOMPtr<nsIEventTarget> mTarget; |
|
173 }; |
|
174 |
|
175 NS_IMPL_ISUPPORTS(nsOutputStreamReadyEvent, nsIRunnable, |
|
176 nsIOutputStreamCallback) |
|
177 |
|
178 //----------------------------------------------------------------------------- |
|
179 |
|
180 already_AddRefed<nsIInputStreamCallback> |
|
181 NS_NewInputStreamReadyEvent(nsIInputStreamCallback *callback, |
|
182 nsIEventTarget *target) |
|
183 { |
|
184 NS_ASSERTION(callback, "null callback"); |
|
185 NS_ASSERTION(target, "null target"); |
|
186 nsRefPtr<nsInputStreamReadyEvent> ev = |
|
187 new nsInputStreamReadyEvent(callback, target); |
|
188 return ev.forget(); |
|
189 } |
|
190 |
|
191 already_AddRefed<nsIOutputStreamCallback> |
|
192 NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback *callback, |
|
193 nsIEventTarget *target) |
|
194 { |
|
195 NS_ASSERTION(callback, "null callback"); |
|
196 NS_ASSERTION(target, "null target"); |
|
197 nsRefPtr<nsOutputStreamReadyEvent> ev = |
|
198 new nsOutputStreamReadyEvent(callback, target); |
|
199 return ev.forget(); |
|
200 } |
|
201 |
|
202 //----------------------------------------------------------------------------- |
|
203 // NS_AsyncCopy implementation |
|
204 |
|
205 // abstract stream copier... |
|
206 class nsAStreamCopier : public nsIInputStreamCallback |
|
207 , public nsIOutputStreamCallback |
|
208 , public nsIRunnable |
|
209 { |
|
210 public: |
|
211 NS_DECL_THREADSAFE_ISUPPORTS |
|
212 |
|
213 nsAStreamCopier() |
|
214 : mLock("nsAStreamCopier.mLock") |
|
215 , mCallback(nullptr) |
|
216 , mProgressCallback(nullptr) |
|
217 , mClosure(nullptr) |
|
218 , mChunkSize(0) |
|
219 , mEventInProcess(false) |
|
220 , mEventIsPending(false) |
|
221 , mCloseSource(true) |
|
222 , mCloseSink(true) |
|
223 , mCanceled(false) |
|
224 , mCancelStatus(NS_OK) |
|
225 { |
|
226 } |
|
227 |
|
228 // virtual since subclasses call superclass Release() |
|
229 virtual ~nsAStreamCopier() |
|
230 { |
|
231 } |
|
232 |
|
233 // kick off the async copy... |
|
234 nsresult Start(nsIInputStream *source, |
|
235 nsIOutputStream *sink, |
|
236 nsIEventTarget *target, |
|
237 nsAsyncCopyCallbackFun callback, |
|
238 void *closure, |
|
239 uint32_t chunksize, |
|
240 bool closeSource, |
|
241 bool closeSink, |
|
242 nsAsyncCopyProgressFun progressCallback) |
|
243 { |
|
244 mSource = source; |
|
245 mSink = sink; |
|
246 mTarget = target; |
|
247 mCallback = callback; |
|
248 mClosure = closure; |
|
249 mChunkSize = chunksize; |
|
250 mCloseSource = closeSource; |
|
251 mCloseSink = closeSink; |
|
252 mProgressCallback = progressCallback; |
|
253 |
|
254 mAsyncSource = do_QueryInterface(mSource); |
|
255 mAsyncSink = do_QueryInterface(mSink); |
|
256 |
|
257 return PostContinuationEvent(); |
|
258 } |
|
259 |
|
260 // implemented by subclasses, returns number of bytes copied and |
|
261 // sets source and sink condition before returning. |
|
262 virtual uint32_t DoCopy(nsresult *sourceCondition, nsresult *sinkCondition) = 0; |
|
263 |
|
264 void Process() |
|
265 { |
|
266 if (!mSource || !mSink) |
|
267 return; |
|
268 |
|
269 nsresult sourceCondition, sinkCondition; |
|
270 nsresult cancelStatus; |
|
271 bool canceled; |
|
272 { |
|
273 MutexAutoLock lock(mLock); |
|
274 canceled = mCanceled; |
|
275 cancelStatus = mCancelStatus; |
|
276 } |
|
277 |
|
278 // Copy data from the source to the sink until we hit failure or have |
|
279 // copied all the data. |
|
280 for (;;) { |
|
281 // Note: copyFailed will be true if the source or the sink have |
|
282 // reported an error, or if we failed to write any bytes |
|
283 // because we have consumed all of our data. |
|
284 bool copyFailed = false; |
|
285 if (!canceled) { |
|
286 uint32_t n = DoCopy(&sourceCondition, &sinkCondition); |
|
287 if (n > 0 && mProgressCallback) { |
|
288 mProgressCallback(mClosure, n); |
|
289 } |
|
290 copyFailed = NS_FAILED(sourceCondition) || |
|
291 NS_FAILED(sinkCondition) || n == 0; |
|
292 |
|
293 MutexAutoLock lock(mLock); |
|
294 canceled = mCanceled; |
|
295 cancelStatus = mCancelStatus; |
|
296 } |
|
297 if (copyFailed && !canceled) { |
|
298 if (sourceCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSource) { |
|
299 // need to wait for more data from source. while waiting for |
|
300 // more source data, be sure to observe failures on output end. |
|
301 mAsyncSource->AsyncWait(this, 0, 0, nullptr); |
|
302 |
|
303 if (mAsyncSink) |
|
304 mAsyncSink->AsyncWait(this, |
|
305 nsIAsyncOutputStream::WAIT_CLOSURE_ONLY, |
|
306 0, nullptr); |
|
307 break; |
|
308 } |
|
309 else if (sinkCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSink) { |
|
310 // need to wait for more room in the sink. while waiting for |
|
311 // more room in the sink, be sure to observer failures on the |
|
312 // input end. |
|
313 mAsyncSink->AsyncWait(this, 0, 0, nullptr); |
|
314 |
|
315 if (mAsyncSource) |
|
316 mAsyncSource->AsyncWait(this, |
|
317 nsIAsyncInputStream::WAIT_CLOSURE_ONLY, |
|
318 0, nullptr); |
|
319 break; |
|
320 } |
|
321 } |
|
322 if (copyFailed || canceled) { |
|
323 if (mCloseSource) { |
|
324 // close source |
|
325 if (mAsyncSource) |
|
326 mAsyncSource->CloseWithStatus(canceled ? cancelStatus : |
|
327 sinkCondition); |
|
328 else |
|
329 mSource->Close(); |
|
330 } |
|
331 mAsyncSource = nullptr; |
|
332 mSource = nullptr; |
|
333 |
|
334 if (mCloseSink) { |
|
335 // close sink |
|
336 if (mAsyncSink) |
|
337 mAsyncSink->CloseWithStatus(canceled ? cancelStatus : |
|
338 sourceCondition); |
|
339 else { |
|
340 // If we have an nsISafeOutputStream, and our |
|
341 // sourceCondition and sinkCondition are not set to a |
|
342 // failure state, finish writing. |
|
343 nsCOMPtr<nsISafeOutputStream> sostream = |
|
344 do_QueryInterface(mSink); |
|
345 if (sostream && NS_SUCCEEDED(sourceCondition) && |
|
346 NS_SUCCEEDED(sinkCondition)) |
|
347 sostream->Finish(); |
|
348 else |
|
349 mSink->Close(); |
|
350 } |
|
351 } |
|
352 mAsyncSink = nullptr; |
|
353 mSink = nullptr; |
|
354 |
|
355 // notify state complete... |
|
356 if (mCallback) { |
|
357 nsresult status; |
|
358 if (!canceled) { |
|
359 status = sourceCondition; |
|
360 if (NS_SUCCEEDED(status)) |
|
361 status = sinkCondition; |
|
362 if (status == NS_BASE_STREAM_CLOSED) |
|
363 status = NS_OK; |
|
364 } else { |
|
365 status = cancelStatus; |
|
366 } |
|
367 mCallback(mClosure, status); |
|
368 } |
|
369 break; |
|
370 } |
|
371 } |
|
372 } |
|
373 |
|
374 nsresult Cancel(nsresult aReason) |
|
375 { |
|
376 MutexAutoLock lock(mLock); |
|
377 if (mCanceled) |
|
378 return NS_ERROR_FAILURE; |
|
379 |
|
380 if (NS_SUCCEEDED(aReason)) { |
|
381 NS_WARNING("cancel with non-failure status code"); |
|
382 aReason = NS_BASE_STREAM_CLOSED; |
|
383 } |
|
384 |
|
385 mCanceled = true; |
|
386 mCancelStatus = aReason; |
|
387 return NS_OK; |
|
388 } |
|
389 |
|
390 NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *source) |
|
391 { |
|
392 PostContinuationEvent(); |
|
393 return NS_OK; |
|
394 } |
|
395 |
|
396 NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *sink) |
|
397 { |
|
398 PostContinuationEvent(); |
|
399 return NS_OK; |
|
400 } |
|
401 |
|
402 // continuation event handler |
|
403 NS_IMETHOD Run() |
|
404 { |
|
405 Process(); |
|
406 |
|
407 // clear "in process" flag and post any pending continuation event |
|
408 MutexAutoLock lock(mLock); |
|
409 mEventInProcess = false; |
|
410 if (mEventIsPending) { |
|
411 mEventIsPending = false; |
|
412 PostContinuationEvent_Locked(); |
|
413 } |
|
414 |
|
415 return NS_OK; |
|
416 } |
|
417 |
|
418 nsresult PostContinuationEvent() |
|
419 { |
|
420 // we cannot post a continuation event if there is currently |
|
421 // an event in process. doing so could result in Process being |
|
422 // run simultaneously on multiple threads, so we mark the event |
|
423 // as pending, and if an event is already in process then we |
|
424 // just let that existing event take care of posting the real |
|
425 // continuation event. |
|
426 |
|
427 MutexAutoLock lock(mLock); |
|
428 return PostContinuationEvent_Locked(); |
|
429 } |
|
430 |
|
431 nsresult PostContinuationEvent_Locked() |
|
432 { |
|
433 nsresult rv = NS_OK; |
|
434 if (mEventInProcess) |
|
435 mEventIsPending = true; |
|
436 else { |
|
437 rv = mTarget->Dispatch(this, NS_DISPATCH_NORMAL); |
|
438 if (NS_SUCCEEDED(rv)) |
|
439 mEventInProcess = true; |
|
440 else |
|
441 NS_WARNING("unable to post continuation event"); |
|
442 } |
|
443 return rv; |
|
444 } |
|
445 |
|
446 protected: |
|
447 nsCOMPtr<nsIInputStream> mSource; |
|
448 nsCOMPtr<nsIOutputStream> mSink; |
|
449 nsCOMPtr<nsIAsyncInputStream> mAsyncSource; |
|
450 nsCOMPtr<nsIAsyncOutputStream> mAsyncSink; |
|
451 nsCOMPtr<nsIEventTarget> mTarget; |
|
452 Mutex mLock; |
|
453 nsAsyncCopyCallbackFun mCallback; |
|
454 nsAsyncCopyProgressFun mProgressCallback; |
|
455 void *mClosure; |
|
456 uint32_t mChunkSize; |
|
457 bool mEventInProcess; |
|
458 bool mEventIsPending; |
|
459 bool mCloseSource; |
|
460 bool mCloseSink; |
|
461 bool mCanceled; |
|
462 nsresult mCancelStatus; |
|
463 }; |
|
464 |
|
465 NS_IMPL_ISUPPORTS(nsAStreamCopier, |
|
466 nsIInputStreamCallback, |
|
467 nsIOutputStreamCallback, |
|
468 nsIRunnable) |
|
469 |
|
470 class nsStreamCopierIB MOZ_FINAL : public nsAStreamCopier |
|
471 { |
|
472 public: |
|
473 nsStreamCopierIB() : nsAStreamCopier() {} |
|
474 virtual ~nsStreamCopierIB() {} |
|
475 |
|
476 struct ReadSegmentsState { |
|
477 nsIOutputStream *mSink; |
|
478 nsresult mSinkCondition; |
|
479 }; |
|
480 |
|
481 static NS_METHOD ConsumeInputBuffer(nsIInputStream *inStr, |
|
482 void *closure, |
|
483 const char *buffer, |
|
484 uint32_t offset, |
|
485 uint32_t count, |
|
486 uint32_t *countWritten) |
|
487 { |
|
488 ReadSegmentsState *state = (ReadSegmentsState *) closure; |
|
489 |
|
490 nsresult rv = state->mSink->Write(buffer, count, countWritten); |
|
491 if (NS_FAILED(rv)) |
|
492 state->mSinkCondition = rv; |
|
493 else if (*countWritten == 0) |
|
494 state->mSinkCondition = NS_BASE_STREAM_CLOSED; |
|
495 |
|
496 return state->mSinkCondition; |
|
497 } |
|
498 |
|
499 uint32_t DoCopy(nsresult *sourceCondition, nsresult *sinkCondition) |
|
500 { |
|
501 ReadSegmentsState state; |
|
502 state.mSink = mSink; |
|
503 state.mSinkCondition = NS_OK; |
|
504 |
|
505 uint32_t n; |
|
506 *sourceCondition = |
|
507 mSource->ReadSegments(ConsumeInputBuffer, &state, mChunkSize, &n); |
|
508 *sinkCondition = state.mSinkCondition; |
|
509 return n; |
|
510 } |
|
511 }; |
|
512 |
|
513 class nsStreamCopierOB MOZ_FINAL : public nsAStreamCopier |
|
514 { |
|
515 public: |
|
516 nsStreamCopierOB() : nsAStreamCopier() {} |
|
517 virtual ~nsStreamCopierOB() {} |
|
518 |
|
519 struct WriteSegmentsState { |
|
520 nsIInputStream *mSource; |
|
521 nsresult mSourceCondition; |
|
522 }; |
|
523 |
|
524 static NS_METHOD FillOutputBuffer(nsIOutputStream *outStr, |
|
525 void *closure, |
|
526 char *buffer, |
|
527 uint32_t offset, |
|
528 uint32_t count, |
|
529 uint32_t *countRead) |
|
530 { |
|
531 WriteSegmentsState *state = (WriteSegmentsState *) closure; |
|
532 |
|
533 nsresult rv = state->mSource->Read(buffer, count, countRead); |
|
534 if (NS_FAILED(rv)) |
|
535 state->mSourceCondition = rv; |
|
536 else if (*countRead == 0) |
|
537 state->mSourceCondition = NS_BASE_STREAM_CLOSED; |
|
538 |
|
539 return state->mSourceCondition; |
|
540 } |
|
541 |
|
542 uint32_t DoCopy(nsresult *sourceCondition, nsresult *sinkCondition) |
|
543 { |
|
544 WriteSegmentsState state; |
|
545 state.mSource = mSource; |
|
546 state.mSourceCondition = NS_OK; |
|
547 |
|
548 uint32_t n; |
|
549 *sinkCondition = |
|
550 mSink->WriteSegments(FillOutputBuffer, &state, mChunkSize, &n); |
|
551 *sourceCondition = state.mSourceCondition; |
|
552 return n; |
|
553 } |
|
554 }; |
|
555 |
|
556 //----------------------------------------------------------------------------- |
|
557 |
|
558 nsresult |
|
559 NS_AsyncCopy(nsIInputStream *source, |
|
560 nsIOutputStream *sink, |
|
561 nsIEventTarget *target, |
|
562 nsAsyncCopyMode mode, |
|
563 uint32_t chunkSize, |
|
564 nsAsyncCopyCallbackFun callback, |
|
565 void *closure, |
|
566 bool closeSource, |
|
567 bool closeSink, |
|
568 nsISupports **aCopierCtx, |
|
569 nsAsyncCopyProgressFun progressCallback) |
|
570 { |
|
571 NS_ASSERTION(target, "non-null target required"); |
|
572 |
|
573 nsresult rv; |
|
574 nsAStreamCopier *copier; |
|
575 |
|
576 if (mode == NS_ASYNCCOPY_VIA_READSEGMENTS) |
|
577 copier = new nsStreamCopierIB(); |
|
578 else |
|
579 copier = new nsStreamCopierOB(); |
|
580 |
|
581 if (!copier) |
|
582 return NS_ERROR_OUT_OF_MEMORY; |
|
583 |
|
584 // Start() takes an owning ref to the copier... |
|
585 NS_ADDREF(copier); |
|
586 rv = copier->Start(source, sink, target, callback, closure, chunkSize, |
|
587 closeSource, closeSink, progressCallback); |
|
588 |
|
589 if (aCopierCtx) { |
|
590 *aCopierCtx = static_cast<nsISupports*>( |
|
591 static_cast<nsIRunnable*>(copier)); |
|
592 NS_ADDREF(*aCopierCtx); |
|
593 } |
|
594 NS_RELEASE(copier); |
|
595 |
|
596 return rv; |
|
597 } |
|
598 |
|
599 //----------------------------------------------------------------------------- |
|
600 |
|
601 nsresult |
|
602 NS_CancelAsyncCopy(nsISupports *aCopierCtx, nsresult aReason) |
|
603 { |
|
604 nsAStreamCopier *copier = static_cast<nsAStreamCopier *>( |
|
605 static_cast<nsIRunnable *>(aCopierCtx)); |
|
606 return copier->Cancel(aReason); |
|
607 } |
|
608 |
|
609 //----------------------------------------------------------------------------- |
|
610 |
|
611 nsresult |
|
612 NS_ConsumeStream(nsIInputStream *stream, uint32_t maxCount, nsACString &result) |
|
613 { |
|
614 nsresult rv = NS_OK; |
|
615 result.Truncate(); |
|
616 |
|
617 while (maxCount) { |
|
618 uint64_t avail64; |
|
619 rv = stream->Available(&avail64); |
|
620 if (NS_FAILED(rv)) { |
|
621 if (rv == NS_BASE_STREAM_CLOSED) |
|
622 rv = NS_OK; |
|
623 break; |
|
624 } |
|
625 if (avail64 == 0) |
|
626 break; |
|
627 |
|
628 uint32_t avail = (uint32_t)XPCOM_MIN<uint64_t>(avail64, maxCount); |
|
629 |
|
630 // resize result buffer |
|
631 uint32_t length = result.Length(); |
|
632 if (avail > UINT32_MAX - length) |
|
633 return NS_ERROR_FILE_TOO_BIG; |
|
634 |
|
635 result.SetLength(length + avail); |
|
636 if (result.Length() != (length + avail)) |
|
637 return NS_ERROR_OUT_OF_MEMORY; |
|
638 char *buf = result.BeginWriting() + length; |
|
639 |
|
640 uint32_t n; |
|
641 rv = stream->Read(buf, avail, &n); |
|
642 if (NS_FAILED(rv)) |
|
643 break; |
|
644 if (n != avail) |
|
645 result.SetLength(length + n); |
|
646 if (n == 0) |
|
647 break; |
|
648 maxCount -= n; |
|
649 } |
|
650 |
|
651 return rv; |
|
652 } |
|
653 |
|
654 //----------------------------------------------------------------------------- |
|
655 |
|
656 static NS_METHOD |
|
657 TestInputStream(nsIInputStream *inStr, |
|
658 void *closure, |
|
659 const char *buffer, |
|
660 uint32_t offset, |
|
661 uint32_t count, |
|
662 uint32_t *countWritten) |
|
663 { |
|
664 bool *result = static_cast<bool *>(closure); |
|
665 *result = true; |
|
666 return NS_ERROR_ABORT; // don't call me anymore |
|
667 } |
|
668 |
|
669 bool |
|
670 NS_InputStreamIsBuffered(nsIInputStream *stream) |
|
671 { |
|
672 nsCOMPtr<nsIBufferedInputStream> bufferedIn = do_QueryInterface(stream); |
|
673 if (bufferedIn) { |
|
674 return true; |
|
675 } |
|
676 |
|
677 bool result = false; |
|
678 uint32_t n; |
|
679 nsresult rv = stream->ReadSegments(TestInputStream, |
|
680 &result, 1, &n); |
|
681 return result || NS_SUCCEEDED(rv); |
|
682 } |
|
683 |
|
684 static NS_METHOD |
|
685 TestOutputStream(nsIOutputStream *outStr, |
|
686 void *closure, |
|
687 char *buffer, |
|
688 uint32_t offset, |
|
689 uint32_t count, |
|
690 uint32_t *countRead) |
|
691 { |
|
692 bool *result = static_cast<bool *>(closure); |
|
693 *result = true; |
|
694 return NS_ERROR_ABORT; // don't call me anymore |
|
695 } |
|
696 |
|
697 bool |
|
698 NS_OutputStreamIsBuffered(nsIOutputStream *stream) |
|
699 { |
|
700 nsCOMPtr<nsIBufferedOutputStream> bufferedOut = do_QueryInterface(stream); |
|
701 if (bufferedOut) { |
|
702 return true; |
|
703 } |
|
704 |
|
705 bool result = false; |
|
706 uint32_t n; |
|
707 stream->WriteSegments(TestOutputStream, &result, 1, &n); |
|
708 return result; |
|
709 } |
|
710 |
|
711 //----------------------------------------------------------------------------- |
|
712 |
|
713 NS_METHOD |
|
714 NS_CopySegmentToStream(nsIInputStream *inStr, |
|
715 void *closure, |
|
716 const char *buffer, |
|
717 uint32_t offset, |
|
718 uint32_t count, |
|
719 uint32_t *countWritten) |
|
720 { |
|
721 nsIOutputStream *outStr = static_cast<nsIOutputStream *>(closure); |
|
722 *countWritten = 0; |
|
723 while (count) { |
|
724 uint32_t n; |
|
725 nsresult rv = outStr->Write(buffer, count, &n); |
|
726 if (NS_FAILED(rv)) |
|
727 return rv; |
|
728 buffer += n; |
|
729 count -= n; |
|
730 *countWritten += n; |
|
731 } |
|
732 return NS_OK; |
|
733 } |
|
734 |
|
735 NS_METHOD |
|
736 NS_CopySegmentToBuffer(nsIInputStream *inStr, |
|
737 void *closure, |
|
738 const char *buffer, |
|
739 uint32_t offset, |
|
740 uint32_t count, |
|
741 uint32_t *countWritten) |
|
742 { |
|
743 char *toBuf = static_cast<char *>(closure); |
|
744 memcpy(&toBuf[offset], buffer, count); |
|
745 *countWritten = count; |
|
746 return NS_OK; |
|
747 } |
|
748 |
|
749 NS_METHOD |
|
750 NS_CopySegmentToBuffer(nsIOutputStream *outStr, |
|
751 void *closure, |
|
752 char *buffer, |
|
753 uint32_t offset, |
|
754 uint32_t count, |
|
755 uint32_t *countRead) |
|
756 { |
|
757 const char* fromBuf = static_cast<const char*>(closure); |
|
758 memcpy(buffer, &fromBuf[offset], count); |
|
759 *countRead = count; |
|
760 return NS_OK; |
|
761 } |
|
762 |
|
763 NS_METHOD |
|
764 NS_DiscardSegment(nsIInputStream *inStr, |
|
765 void *closure, |
|
766 const char *buffer, |
|
767 uint32_t offset, |
|
768 uint32_t count, |
|
769 uint32_t *countWritten) |
|
770 { |
|
771 *countWritten = count; |
|
772 return NS_OK; |
|
773 } |
|
774 |
|
775 //----------------------------------------------------------------------------- |
|
776 |
|
777 NS_METHOD |
|
778 NS_WriteSegmentThunk(nsIInputStream *inStr, |
|
779 void *closure, |
|
780 const char *buffer, |
|
781 uint32_t offset, |
|
782 uint32_t count, |
|
783 uint32_t *countWritten) |
|
784 { |
|
785 nsWriteSegmentThunk *thunk = static_cast<nsWriteSegmentThunk *>(closure); |
|
786 return thunk->mFun(thunk->mStream, thunk->mClosure, buffer, offset, count, |
|
787 countWritten); |
|
788 } |
|
789 |
|
790 NS_METHOD |
|
791 NS_FillArray(FallibleTArray<char>& aDest, nsIInputStream *aInput, |
|
792 uint32_t aKeep, uint32_t *aNewBytes) |
|
793 { |
|
794 MOZ_ASSERT(aInput, "null stream"); |
|
795 MOZ_ASSERT(aKeep <= aDest.Length(), "illegal keep count"); |
|
796 |
|
797 char* aBuffer = aDest.Elements(); |
|
798 int64_t keepOffset = int64_t(aDest.Length()) - aKeep; |
|
799 if (0 != aKeep && keepOffset > 0) { |
|
800 memmove(aBuffer, aBuffer + keepOffset, aKeep); |
|
801 } |
|
802 |
|
803 nsresult rv = |
|
804 aInput->Read(aBuffer + aKeep, aDest.Capacity() - aKeep, aNewBytes); |
|
805 if (NS_FAILED(rv)) { |
|
806 *aNewBytes = 0; |
|
807 } |
|
808 // NOTE: we rely on the fact that the new slots are NOT initialized by |
|
809 // SetLengthAndRetainStorage here, see nsTArrayElementTraits::Construct() |
|
810 // in nsTArray.h: |
|
811 aDest.SetLengthAndRetainStorage(aKeep + *aNewBytes); |
|
812 |
|
813 MOZ_ASSERT(aDest.Length() <= aDest.Capacity(), "buffer overflow"); |
|
814 return rv; |
|
815 } |