Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "ipc/IPCMessageUtils.h"
7 :
8 : #include "nsBufferedStreams.h"
9 : #include "nsStreamUtils.h"
10 : #include "nsNetCID.h"
11 : #include "nsIClassInfoImpl.h"
12 : #include "mozilla/ipc/InputStreamUtils.h"
13 : #include <algorithm>
14 :
15 : #ifdef DEBUG_brendan
16 : # define METERING
17 : #endif
18 :
19 : #ifdef METERING
20 : # include <stdio.h>
21 : # define METER(x) x
22 : # define MAX_BIG_SEEKS 20
23 :
24 : static struct {
25 : uint32_t mSeeksWithinBuffer;
26 : uint32_t mSeeksOutsideBuffer;
27 : uint32_t mBufferReadUponSeek;
28 : uint32_t mBufferUnreadUponSeek;
29 : uint32_t mBytesReadFromBuffer;
30 : uint32_t mBigSeekIndex;
31 : struct {
32 : int64_t mOldOffset;
33 : int64_t mNewOffset;
34 : } mBigSeek[MAX_BIG_SEEKS];
35 : } bufstats;
36 : #else
37 : # define METER(x) /* nothing */
38 : #endif
39 :
40 : using namespace mozilla::ipc;
41 : using mozilla::Maybe;
42 : using mozilla::Nothing;
43 : using mozilla::Some;
44 :
45 : ////////////////////////////////////////////////////////////////////////////////
46 : // nsBufferedStream
47 :
48 113 : nsBufferedStream::nsBufferedStream()
49 : : mBuffer(nullptr),
50 : mBufferStartOffset(0),
51 : mCursor(0),
52 : mFillPoint(0),
53 : mStream(nullptr),
54 : mBufferDisabled(false),
55 : mEOF(false),
56 113 : mGetBufferCount(0)
57 : {
58 113 : }
59 :
60 226 : nsBufferedStream::~nsBufferedStream()
61 : {
62 113 : Close();
63 113 : }
64 :
65 1335 : NS_IMPL_ISUPPORTS(nsBufferedStream, nsISeekableStream)
66 :
67 : nsresult
68 113 : nsBufferedStream::Init(nsISupports* stream, uint32_t bufferSize)
69 : {
70 113 : NS_ASSERTION(stream, "need to supply a stream");
71 113 : NS_ASSERTION(mStream == nullptr, "already inited");
72 113 : mStream = stream;
73 113 : NS_IF_ADDREF(mStream);
74 113 : mBufferSize = bufferSize;
75 113 : mBufferStartOffset = 0;
76 113 : mCursor = 0;
77 226 : mBuffer = new (mozilla::fallible) char[bufferSize];
78 113 : if (mBuffer == nullptr) {
79 0 : return NS_ERROR_OUT_OF_MEMORY;
80 : }
81 113 : return NS_OK;
82 : }
83 :
84 : nsresult
85 143 : nsBufferedStream::Close()
86 : {
87 143 : NS_IF_RELEASE(mStream);
88 143 : if (mBuffer) {
89 113 : delete[] mBuffer;
90 113 : mBuffer = nullptr;
91 113 : mBufferSize = 0;
92 113 : mBufferStartOffset = 0;
93 113 : mCursor = 0;
94 113 : mFillPoint = 0;
95 : }
96 : #ifdef METERING
97 : {
98 : static FILE *tfp;
99 : if (!tfp) {
100 : tfp = fopen("/tmp/bufstats", "w");
101 : if (tfp) {
102 : setvbuf(tfp, nullptr, _IOLBF, 0);
103 : }
104 : }
105 : if (tfp) {
106 : fprintf(tfp, "seeks within buffer: %u\n",
107 : bufstats.mSeeksWithinBuffer);
108 : fprintf(tfp, "seeks outside buffer: %u\n",
109 : bufstats.mSeeksOutsideBuffer);
110 : fprintf(tfp, "buffer read on seek: %u\n",
111 : bufstats.mBufferReadUponSeek);
112 : fprintf(tfp, "buffer unread on seek: %u\n",
113 : bufstats.mBufferUnreadUponSeek);
114 : fprintf(tfp, "bytes read from buffer: %u\n",
115 : bufstats.mBytesReadFromBuffer);
116 : for (uint32_t i = 0; i < bufstats.mBigSeekIndex; i++) {
117 : fprintf(tfp, "bigseek[%u] = {old: %u, new: %u}\n",
118 : i,
119 : bufstats.mBigSeek[i].mOldOffset,
120 : bufstats.mBigSeek[i].mNewOffset);
121 : }
122 : }
123 : }
124 : #endif
125 143 : return NS_OK;
126 : }
127 :
128 : NS_IMETHODIMP
129 102 : nsBufferedStream::Seek(int32_t whence, int64_t offset)
130 : {
131 102 : if (mStream == nullptr) {
132 0 : return NS_BASE_STREAM_CLOSED;
133 : }
134 :
135 : // If the underlying stream isn't a random access store, then fail early.
136 : // We could possibly succeed for the case where the seek position denotes
137 : // something that happens to be read into the buffer, but that would make
138 : // the failure data-dependent.
139 : nsresult rv;
140 204 : nsCOMPtr<nsISeekableStream> ras = do_QueryInterface(mStream, &rv);
141 102 : if (NS_FAILED(rv)) {
142 : #ifdef DEBUG
143 0 : NS_ERROR("mStream doesn't QI to nsISeekableStream");
144 : #endif
145 0 : return rv;
146 : }
147 :
148 102 : int64_t absPos = 0;
149 102 : switch (whence) {
150 : case nsISeekableStream::NS_SEEK_SET:
151 102 : absPos = offset;
152 102 : break;
153 : case nsISeekableStream::NS_SEEK_CUR:
154 0 : absPos = mBufferStartOffset;
155 0 : absPos += mCursor;
156 0 : absPos += offset;
157 0 : break;
158 : case nsISeekableStream::NS_SEEK_END:
159 0 : absPos = -1;
160 0 : break;
161 : default:
162 0 : NS_NOTREACHED("bogus seek whence parameter");
163 0 : return NS_ERROR_UNEXPECTED;
164 : }
165 :
166 : // Let mCursor point into the existing buffer if the new position is
167 : // between the current cursor and the mFillPoint "fencepost" -- the
168 : // client may never get around to a Read or Write after this Seek.
169 : // Read and Write worry about flushing and filling in that event.
170 : // But if we're at EOF, make sure to pass the seek through to the
171 : // underlying stream, because it may have auto-closed itself and
172 : // needs to reopen.
173 102 : uint32_t offsetInBuffer = uint32_t(absPos - mBufferStartOffset);
174 102 : if (offsetInBuffer <= mFillPoint && !mEOF) {
175 : METER(bufstats.mSeeksWithinBuffer++);
176 102 : mCursor = offsetInBuffer;
177 102 : return NS_OK;
178 : }
179 :
180 : METER(bufstats.mSeeksOutsideBuffer++);
181 : METER(bufstats.mBufferReadUponSeek += mCursor);
182 : METER(bufstats.mBufferUnreadUponSeek += mFillPoint - mCursor);
183 0 : rv = Flush();
184 0 : if (NS_FAILED(rv)) {
185 : #ifdef DEBUG
186 0 : NS_WARNING("(debug) Flush returned error within nsBufferedStream::Seek, so we exit early.");
187 : #endif
188 0 : return rv;
189 : }
190 :
191 0 : rv = ras->Seek(whence, offset);
192 0 : if (NS_FAILED(rv)) {
193 : #ifdef DEBUG
194 0 : NS_WARNING("(debug) Error: ras->Seek() returned error within nsBufferedStream::Seek, so we exit early.");
195 : #endif
196 0 : return rv;
197 : }
198 :
199 0 : mEOF = false;
200 :
201 : // Recompute whether the offset we're seeking to is in our buffer.
202 : // Note that we need to recompute because Flush() might have
203 : // changed mBufferStartOffset.
204 0 : offsetInBuffer = uint32_t(absPos - mBufferStartOffset);
205 0 : if (offsetInBuffer <= mFillPoint) {
206 : // It's safe to just set mCursor to offsetInBuffer. In particular, we
207 : // want to avoid calling Fill() here since we already have the data that
208 : // was seeked to and calling Fill() might auto-close our underlying
209 : // stream in some cases.
210 0 : mCursor = offsetInBuffer;
211 0 : return NS_OK;
212 : }
213 :
214 : METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS)
215 : bufstats.mBigSeek[bufstats.mBigSeekIndex].mOldOffset =
216 : mBufferStartOffset + int64_t(mCursor));
217 0 : const int64_t minus1 = -1;
218 0 : if (absPos == minus1) {
219 : // then we had the SEEK_END case, above
220 : int64_t tellPos;
221 0 : rv = ras->Tell(&tellPos);
222 0 : mBufferStartOffset = tellPos;
223 0 : if (NS_FAILED(rv)) {
224 0 : return rv;
225 : }
226 : }
227 : else {
228 0 : mBufferStartOffset = absPos;
229 : }
230 : METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS)
231 : bufstats.mBigSeek[bufstats.mBigSeekIndex++].mNewOffset =
232 : mBufferStartOffset);
233 :
234 0 : mFillPoint = mCursor = 0;
235 0 : return Fill();
236 : }
237 :
238 : NS_IMETHODIMP
239 1 : nsBufferedStream::Tell(int64_t *result)
240 : {
241 1 : if (mStream == nullptr) {
242 0 : return NS_BASE_STREAM_CLOSED;
243 : }
244 :
245 1 : int64_t result64 = mBufferStartOffset;
246 1 : result64 += mCursor;
247 1 : *result = result64;
248 1 : return NS_OK;
249 : }
250 :
251 : NS_IMETHODIMP
252 0 : nsBufferedStream::SetEOF()
253 : {
254 0 : if (mStream == nullptr) {
255 0 : return NS_BASE_STREAM_CLOSED;
256 : }
257 :
258 : nsresult rv;
259 0 : nsCOMPtr<nsISeekableStream> ras = do_QueryInterface(mStream, &rv);
260 0 : if (NS_FAILED(rv)) {
261 0 : return rv;
262 : }
263 :
264 0 : rv = ras->SetEOF();
265 0 : if (NS_SUCCEEDED(rv)) {
266 0 : mEOF = true;
267 : }
268 :
269 0 : return rv;
270 : }
271 :
272 : nsresult
273 0 : nsBufferedStream::GetData(nsISupports **aResult)
274 : {
275 0 : nsCOMPtr<nsISupports> rv(mStream);
276 0 : *aResult = rv.forget().take();
277 0 : return NS_OK;
278 : }
279 :
280 : ////////////////////////////////////////////////////////////////////////////////
281 : // nsBufferedInputStream
282 :
283 562 : NS_IMPL_ADDREF_INHERITED(nsBufferedInputStream, nsBufferedStream)
284 562 : NS_IMPL_RELEASE_INHERITED(nsBufferedInputStream, nsBufferedStream)
285 :
286 3 : NS_IMPL_CLASSINFO(nsBufferedInputStream, nullptr, nsIClassInfo::THREADSAFE,
287 : NS_BUFFEREDINPUTSTREAM_CID)
288 :
289 461 : NS_INTERFACE_MAP_BEGIN(nsBufferedInputStream)
290 461 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIInputStream, nsIBufferedInputStream)
291 306 : NS_INTERFACE_MAP_ENTRY(nsIBufferedInputStream)
292 103 : NS_INTERFACE_MAP_ENTRY(nsIStreamBufferAccess)
293 103 : NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream, IsIPCSerializable())
294 103 : NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream, IsAsyncInputStream())
295 103 : NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback, IsAsyncInputStream())
296 103 : NS_IMPL_QUERY_CLASSINFO(nsBufferedInputStream)
297 103 : NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream)
298 :
299 0 : NS_IMPL_CI_INTERFACE_GETTER(nsBufferedInputStream,
300 : nsIInputStream,
301 : nsIBufferedInputStream,
302 : nsISeekableStream,
303 : nsIStreamBufferAccess)
304 :
305 : nsresult
306 101 : nsBufferedInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
307 : {
308 101 : NS_ENSURE_NO_AGGREGATION(aOuter);
309 :
310 101 : nsBufferedInputStream* stream = new nsBufferedInputStream();
311 101 : if (stream == nullptr) {
312 0 : return NS_ERROR_OUT_OF_MEMORY;
313 : }
314 101 : NS_ADDREF(stream);
315 101 : nsresult rv = stream->QueryInterface(aIID, aResult);
316 101 : NS_RELEASE(stream);
317 101 : return rv;
318 : }
319 :
320 : NS_IMETHODIMP
321 101 : nsBufferedInputStream::Init(nsIInputStream* stream, uint32_t bufferSize)
322 : {
323 101 : return nsBufferedStream::Init(stream, bufferSize);
324 : }
325 :
326 : NS_IMETHODIMP
327 6 : nsBufferedInputStream::Close()
328 : {
329 6 : nsresult rv1 = NS_OK, rv2;
330 6 : if (mStream) {
331 6 : rv1 = Source()->Close();
332 : #ifdef DEBUG
333 6 : if (NS_FAILED(rv1)) {
334 0 : NS_WARNING("(debug) Error: Source()->Close() returned error (rv1) in bsBuffedInputStream::Close().");
335 : };
336 : #endif
337 6 : NS_RELEASE(mStream);
338 : }
339 :
340 6 : rv2 = nsBufferedStream::Close();
341 :
342 : #ifdef DEBUG
343 6 : if (NS_FAILED(rv2)) {
344 0 : NS_WARNING("(debug) Error: nsBufferedStream::Close() returned error (rv2) within nsBufferedInputStream::Close().");
345 : };
346 : #endif
347 :
348 6 : mAsyncWaitCallback = nullptr;
349 :
350 6 : if (NS_FAILED(rv1)) {
351 0 : return rv1;
352 : }
353 6 : return rv2;
354 : }
355 :
356 : NS_IMETHODIMP
357 98 : nsBufferedInputStream::Available(uint64_t *result)
358 : {
359 98 : nsresult rv = NS_OK;
360 98 : *result = 0;
361 98 : if (mStream) {
362 98 : rv = Source()->Available(result);
363 : }
364 98 : *result += (mFillPoint - mCursor);
365 98 : return rv;
366 : }
367 :
368 : NS_IMETHODIMP
369 369 : nsBufferedInputStream::Read(char * buf, uint32_t count, uint32_t *result)
370 : {
371 369 : if (mBufferDisabled) {
372 0 : if (!mStream) {
373 0 : *result = 0;
374 0 : return NS_OK;
375 : }
376 0 : nsresult rv = Source()->Read(buf, count, result);
377 0 : if (NS_SUCCEEDED(rv)) {
378 0 : mBufferStartOffset += *result; // so nsBufferedStream::Tell works
379 0 : if (*result == 0) {
380 0 : mEOF = true;
381 : }
382 : }
383 0 : return rv;
384 : }
385 :
386 369 : return ReadSegments(NS_CopySegmentToBuffer, buf, count, result);
387 : }
388 :
389 : NS_IMETHODIMP
390 404 : nsBufferedInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure,
391 : uint32_t count, uint32_t *result)
392 : {
393 404 : *result = 0;
394 :
395 404 : if (!mStream) {
396 0 : return NS_OK;
397 : }
398 :
399 404 : nsresult rv = NS_OK;
400 1328 : while (count > 0) {
401 464 : uint32_t amt = std::min(count, mFillPoint - mCursor);
402 464 : if (amt > 0) {
403 361 : uint32_t read = 0;
404 722 : rv = writer(static_cast<nsIBufferedInputStream*>(this), closure,
405 361 : mBuffer + mCursor, *result, amt, &read);
406 361 : if (NS_FAILED(rv)) {
407 : // errors returned from the writer end here!
408 0 : rv = NS_OK;
409 0 : break;
410 : }
411 361 : *result += read;
412 361 : count -= read;
413 361 : mCursor += read;
414 : }
415 : else {
416 103 : rv = Fill();
417 103 : if (NS_FAILED(rv) || mFillPoint == mCursor) {
418 2 : break;
419 : }
420 : }
421 : }
422 404 : return (*result > 0) ? NS_OK : rv;
423 : }
424 :
425 : NS_IMETHODIMP
426 0 : nsBufferedInputStream::IsNonBlocking(bool *aNonBlocking)
427 : {
428 0 : if (mStream) {
429 0 : return Source()->IsNonBlocking(aNonBlocking);
430 : }
431 0 : return NS_ERROR_NOT_INITIALIZED;
432 : }
433 :
434 : NS_IMETHODIMP
435 103 : nsBufferedInputStream::Fill()
436 : {
437 103 : if (mBufferDisabled) {
438 0 : return NS_OK;
439 : }
440 103 : NS_ENSURE_TRUE(mStream, NS_ERROR_NOT_INITIALIZED);
441 :
442 : nsresult rv;
443 103 : int32_t rem = int32_t(mFillPoint - mCursor);
444 103 : if (rem > 0) {
445 : // slide the remainder down to the start of the buffer
446 : // |<------------->|<--rem-->|<--->|
447 : // b c f s
448 0 : memcpy(mBuffer, mBuffer + mCursor, rem);
449 : }
450 103 : mBufferStartOffset += mCursor;
451 103 : mFillPoint = rem;
452 103 : mCursor = 0;
453 :
454 : uint32_t amt;
455 103 : rv = Source()->Read(mBuffer + mFillPoint, mBufferSize - mFillPoint, &amt);
456 103 : if (NS_FAILED(rv)) {
457 0 : return rv;
458 : }
459 :
460 103 : if (amt == 0) {
461 2 : mEOF = true;
462 : }
463 :
464 103 : mFillPoint += amt;
465 103 : return NS_OK;
466 : }
467 :
468 : NS_IMETHODIMP_(char*)
469 0 : nsBufferedInputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
470 : {
471 0 : NS_ASSERTION(mGetBufferCount == 0, "nested GetBuffer!");
472 0 : if (mGetBufferCount != 0) {
473 0 : return nullptr;
474 : }
475 :
476 0 : if (mBufferDisabled) {
477 0 : return nullptr;
478 : }
479 :
480 0 : char* buf = mBuffer + mCursor;
481 0 : uint32_t rem = mFillPoint - mCursor;
482 0 : if (rem == 0) {
483 0 : if (NS_FAILED(Fill())) {
484 0 : return nullptr;
485 : }
486 0 : buf = mBuffer + mCursor;
487 0 : rem = mFillPoint - mCursor;
488 : }
489 :
490 0 : uint32_t mod = (NS_PTR_TO_INT32(buf) & aAlignMask);
491 0 : if (mod) {
492 0 : uint32_t pad = aAlignMask + 1 - mod;
493 0 : if (pad > rem) {
494 0 : return nullptr;
495 : }
496 :
497 0 : memset(buf, 0, pad);
498 0 : mCursor += pad;
499 0 : buf += pad;
500 0 : rem -= pad;
501 : }
502 :
503 0 : if (aLength > rem) {
504 0 : return nullptr;
505 : }
506 0 : mGetBufferCount++;
507 0 : return buf;
508 : }
509 :
510 : NS_IMETHODIMP_(void)
511 0 : nsBufferedInputStream::PutBuffer(char* aBuffer, uint32_t aLength)
512 : {
513 0 : NS_ASSERTION(mGetBufferCount == 1, "stray PutBuffer!");
514 0 : if (--mGetBufferCount != 0) {
515 0 : return;
516 : }
517 :
518 0 : NS_ASSERTION(mCursor + aLength <= mFillPoint, "PutBuffer botch");
519 0 : mCursor += aLength;
520 : }
521 :
522 : NS_IMETHODIMP
523 0 : nsBufferedInputStream::DisableBuffering()
524 : {
525 0 : NS_ASSERTION(!mBufferDisabled, "redundant call to DisableBuffering!");
526 0 : NS_ASSERTION(mGetBufferCount == 0,
527 : "DisableBuffer call between GetBuffer and PutBuffer!");
528 0 : if (mGetBufferCount != 0) {
529 0 : return NS_ERROR_UNEXPECTED;
530 : }
531 :
532 : // Empty the buffer so nsBufferedStream::Tell works.
533 0 : mBufferStartOffset += mCursor;
534 0 : mFillPoint = mCursor = 0;
535 0 : mBufferDisabled = true;
536 0 : return NS_OK;
537 : }
538 :
539 : NS_IMETHODIMP
540 0 : nsBufferedInputStream::EnableBuffering()
541 : {
542 0 : NS_ASSERTION(mBufferDisabled, "gratuitous call to EnableBuffering!");
543 0 : mBufferDisabled = false;
544 0 : return NS_OK;
545 : }
546 :
547 : NS_IMETHODIMP
548 0 : nsBufferedInputStream::GetUnbufferedStream(nsISupports* *aStream)
549 : {
550 : // Empty the buffer so subsequent i/o trumps any buffered data.
551 0 : mBufferStartOffset += mCursor;
552 0 : mFillPoint = mCursor = 0;
553 :
554 0 : *aStream = mStream;
555 0 : NS_IF_ADDREF(*aStream);
556 0 : return NS_OK;
557 : }
558 :
559 : void
560 0 : nsBufferedInputStream::Serialize(InputStreamParams& aParams,
561 : FileDescriptorArray& aFileDescriptors)
562 : {
563 0 : BufferedInputStreamParams params;
564 :
565 0 : if (mStream) {
566 0 : nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream);
567 0 : MOZ_ASSERT(stream);
568 :
569 0 : InputStreamParams wrappedParams;
570 0 : InputStreamHelper::SerializeInputStream(stream, wrappedParams,
571 0 : aFileDescriptors);
572 :
573 0 : params.optionalStream() = wrappedParams;
574 : }
575 : else {
576 0 : params.optionalStream() = mozilla::void_t();
577 : }
578 :
579 0 : params.bufferSize() = mBufferSize;
580 :
581 0 : aParams = params;
582 0 : }
583 :
584 : bool
585 0 : nsBufferedInputStream::Deserialize(const InputStreamParams& aParams,
586 : const FileDescriptorArray& aFileDescriptors)
587 : {
588 0 : if (aParams.type() != InputStreamParams::TBufferedInputStreamParams) {
589 0 : NS_ERROR("Received unknown parameters from the other process!");
590 0 : return false;
591 : }
592 :
593 : const BufferedInputStreamParams& params =
594 0 : aParams.get_BufferedInputStreamParams();
595 0 : const OptionalInputStreamParams& wrappedParams = params.optionalStream();
596 :
597 0 : nsCOMPtr<nsIInputStream> stream;
598 0 : if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) {
599 : stream =
600 0 : InputStreamHelper::DeserializeInputStream(wrappedParams.get_InputStreamParams(),
601 0 : aFileDescriptors);
602 0 : if (!stream) {
603 0 : NS_WARNING("Failed to deserialize wrapped stream!");
604 0 : return false;
605 : }
606 : }
607 : else {
608 0 : NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t,
609 : "Unknown type for OptionalInputStreamParams!");
610 : }
611 :
612 0 : nsresult rv = Init(stream, params.bufferSize());
613 0 : NS_ENSURE_SUCCESS(rv, false);
614 :
615 0 : return true;
616 : }
617 :
618 : Maybe<uint64_t>
619 0 : nsBufferedInputStream::ExpectedSerializedLength()
620 : {
621 0 : nsCOMPtr<nsIIPCSerializableInputStream> stream = do_QueryInterface(mStream);
622 0 : if (stream) {
623 0 : return stream->ExpectedSerializedLength();
624 : }
625 0 : return Nothing();
626 : }
627 :
628 : bool
629 103 : nsBufferedInputStream::IsIPCSerializable() const
630 : {
631 103 : if (!mStream) {
632 0 : return true;
633 : }
634 :
635 206 : nsCOMPtr<nsIIPCSerializableInputStream> stream = do_QueryInterface(mStream);
636 103 : return !!stream;
637 : }
638 :
639 : bool
640 206 : nsBufferedInputStream::IsAsyncInputStream() const
641 : {
642 412 : nsCOMPtr<nsIAsyncInputStream> stream = do_QueryInterface(mStream);
643 412 : return !!stream;
644 : }
645 :
646 : NS_IMETHODIMP
647 0 : nsBufferedInputStream::CloseWithStatus(nsresult aStatus)
648 : {
649 0 : return Close();
650 : }
651 :
652 : NS_IMETHODIMP
653 0 : nsBufferedInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
654 : uint32_t aFlags,
655 : uint32_t aRequestedCount,
656 : nsIEventTarget* aEventTarget)
657 : {
658 0 : nsCOMPtr<nsIAsyncInputStream> stream = do_QueryInterface(mStream);
659 0 : if (!stream) {
660 0 : return NS_ERROR_FAILURE;
661 : }
662 :
663 0 : if (mAsyncWaitCallback && aCallback) {
664 0 : return NS_ERROR_FAILURE;
665 : }
666 :
667 0 : mAsyncWaitCallback = aCallback;
668 :
669 0 : if (!mAsyncWaitCallback) {
670 0 : return NS_OK;
671 : }
672 :
673 0 : return stream->AsyncWait(this, aFlags, aRequestedCount, aEventTarget);
674 : }
675 :
676 : NS_IMETHODIMP
677 0 : nsBufferedInputStream::OnInputStreamReady(nsIAsyncInputStream* aStream)
678 : {
679 : // We have been canceled in the meanwhile.
680 0 : if (!mAsyncWaitCallback) {
681 0 : return NS_OK;
682 : }
683 :
684 0 : nsCOMPtr<nsIInputStreamCallback> callback;
685 0 : callback.swap(mAsyncWaitCallback);
686 :
687 0 : return callback->OnInputStreamReady(this);
688 : }
689 :
690 : NS_IMETHODIMP
691 0 : nsBufferedInputStream::GetData(nsIInputStream **aResult)
692 : {
693 0 : nsCOMPtr<nsISupports> stream;
694 0 : nsBufferedStream::GetData(getter_AddRefs(stream));
695 0 : nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(stream);
696 0 : *aResult = inputStream.forget().take();
697 0 : return NS_OK;
698 : }
699 :
700 : ////////////////////////////////////////////////////////////////////////////////
701 : // nsBufferedOutputStream
702 :
703 54 : NS_IMPL_ADDREF_INHERITED(nsBufferedOutputStream, nsBufferedStream)
704 54 : NS_IMPL_RELEASE_INHERITED(nsBufferedOutputStream, nsBufferedStream)
705 : // This QI uses NS_INTERFACE_MAP_ENTRY_CONDITIONAL to check for
706 : // non-nullness of mSafeStream.
707 36 : NS_INTERFACE_MAP_BEGIN(nsBufferedOutputStream)
708 36 : NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
709 18 : NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISafeOutputStream, mSafeStream)
710 18 : NS_INTERFACE_MAP_ENTRY(nsIBufferedOutputStream)
711 0 : NS_INTERFACE_MAP_ENTRY(nsIStreamBufferAccess)
712 0 : NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream)
713 :
714 : nsresult
715 6 : nsBufferedOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
716 : {
717 6 : NS_ENSURE_NO_AGGREGATION(aOuter);
718 :
719 6 : nsBufferedOutputStream* stream = new nsBufferedOutputStream();
720 6 : if (stream == nullptr) {
721 0 : return NS_ERROR_OUT_OF_MEMORY;
722 : }
723 6 : NS_ADDREF(stream);
724 6 : nsresult rv = stream->QueryInterface(aIID, aResult);
725 6 : NS_RELEASE(stream);
726 6 : return rv;
727 : }
728 :
729 : NS_IMETHODIMP
730 12 : nsBufferedOutputStream::Init(nsIOutputStream* stream, uint32_t bufferSize)
731 : {
732 : // QI stream to an nsISafeOutputStream, to see if we should support it
733 12 : mSafeStream = do_QueryInterface(stream);
734 :
735 12 : return nsBufferedStream::Init(stream, bufferSize);
736 : }
737 :
738 : NS_IMETHODIMP
739 18 : nsBufferedOutputStream::Close()
740 : {
741 18 : nsresult rv1, rv2 = NS_OK, rv3;
742 :
743 18 : rv1 = Flush();
744 :
745 : #ifdef DEBUG
746 18 : if (NS_FAILED(rv1)) {
747 0 : NS_WARNING("(debug) Flush() inside nsBufferedOutputStream::Close() returned error (rv1).");
748 : }
749 : #endif
750 :
751 : // If we fail to Flush all the data, then we close anyway and drop the
752 : // remaining data in the buffer. We do this because it's what Unix does
753 : // for fclose and close. However, we report the error from Flush anyway.
754 18 : if (mStream) {
755 6 : rv2 = Sink()->Close();
756 : #ifdef DEBUG
757 6 : if (NS_FAILED(rv2)) {
758 0 : NS_WARNING("(debug) Sink->Close() inside nsBufferedOutputStream::Close() returned error (rv2).");
759 : }
760 : #endif
761 6 : NS_RELEASE(mStream);
762 : }
763 18 : rv3 = nsBufferedStream::Close();
764 :
765 : #ifdef DEBUG
766 18 : if (NS_FAILED(rv3)) {
767 0 : NS_WARNING("(debug) nsBufferedStream:Close() inside nsBufferedOutputStream::Close() returned error (rv3).");
768 : }
769 : #endif
770 :
771 18 : if (NS_FAILED(rv1)) {
772 0 : return rv1;
773 : }
774 18 : if (NS_FAILED(rv2)) {
775 0 : return rv2;
776 : }
777 18 : return rv3;
778 : }
779 :
780 : NS_IMETHODIMP
781 234 : nsBufferedOutputStream::Write(const char *buf, uint32_t count, uint32_t *result)
782 : {
783 234 : nsresult rv = NS_OK;
784 234 : uint32_t written = 0;
785 234 : *result = 0;
786 234 : if (!mStream) {
787 : // We special case this situtaion.
788 : // We should catch the failure, NS_BASE_STREAM_CLOSED ASAP, here.
789 : // If we don't, eventually Flush() is called in the while loop below
790 : // after so many writes.
791 : // However, Flush() returns NS_OK when mStream is null (!!),
792 : // and we don't get a meaningful error, NS_BASE_STREAM_CLOSED,
793 : // soon enough when we use buffered output.
794 : #ifdef DEBUG
795 0 : NS_WARNING("(info) nsBufferedOutputStream::Write returns NS_BASE_STREAM_CLOSED immediately (mStream==null).");
796 : #endif
797 0 : return NS_BASE_STREAM_CLOSED;
798 : }
799 :
800 864 : while (count > 0) {
801 315 : uint32_t amt = std::min(count, mBufferSize - mCursor);
802 315 : if (amt > 0) {
803 229 : memcpy(mBuffer + mCursor, buf + written, amt);
804 229 : written += amt;
805 229 : count -= amt;
806 229 : mCursor += amt;
807 229 : if (mFillPoint < mCursor)
808 229 : mFillPoint = mCursor;
809 : }
810 : else {
811 86 : NS_ASSERTION(mFillPoint, "loop in nsBufferedOutputStream::Write!");
812 86 : rv = Flush();
813 86 : if (NS_FAILED(rv)) {
814 : #ifdef DEBUG
815 0 : NS_WARNING("(debug) Flush() returned error in nsBufferedOutputStream::Write.");
816 : #endif
817 0 : break;
818 : }
819 : }
820 : }
821 234 : *result = written;
822 234 : return (written > 0) ? NS_OK : rv;
823 : }
824 :
825 : NS_IMETHODIMP
826 110 : nsBufferedOutputStream::Flush()
827 : {
828 : nsresult rv;
829 : uint32_t amt;
830 110 : if (!mStream) {
831 : // Stream already cancelled/flushed; probably because of previous error.
832 12 : return NS_OK;
833 : }
834 : // optimize : some code within C-C needs to call Seek -> Flush() often.
835 98 : if (mFillPoint == 0) {
836 0 : return NS_OK;
837 : }
838 98 : rv = Sink()->Write(mBuffer, mFillPoint, &amt);
839 98 : if (NS_FAILED(rv)) {
840 0 : return rv;
841 : }
842 98 : mBufferStartOffset += amt;
843 98 : if (amt == mFillPoint) {
844 98 : mFillPoint = mCursor = 0;
845 98 : return NS_OK; // flushed everything
846 : }
847 :
848 : // slide the remainder down to the start of the buffer
849 : // |<-------------->|<---|----->|
850 : // b a c s
851 0 : uint32_t rem = mFillPoint - amt;
852 0 : memmove(mBuffer, mBuffer + amt, rem);
853 0 : mFillPoint = mCursor = rem;
854 0 : return NS_ERROR_FAILURE; // didn't flush all
855 : }
856 :
857 : // nsISafeOutputStream
858 : NS_IMETHODIMP
859 6 : nsBufferedOutputStream::Finish()
860 : {
861 : // flush the stream, to write out any buffered data...
862 6 : nsresult rv1 = nsBufferedOutputStream::Flush();
863 6 : nsresult rv2 = NS_OK, rv3;
864 :
865 6 : if (NS_FAILED(rv1)) {
866 0 : NS_WARNING("(debug) nsBufferedOutputStream::Flush() failed in nsBufferedOutputStream::Finish()! Possible dataloss.");
867 :
868 0 : rv2 = Sink()->Close();
869 0 : if (NS_FAILED(rv2)) {
870 0 : NS_WARNING("(debug) Sink()->Close() failed in nsBufferedOutputStream::Finish()! Possible dataloss.");
871 : }
872 : } else {
873 6 : rv2 = mSafeStream->Finish();
874 6 : if (NS_FAILED(rv2)) {
875 0 : NS_WARNING("(debug) mSafeStream->Finish() failed within nsBufferedOutputStream::Flush()! Possible dataloss.");
876 : }
877 : }
878 :
879 : // ... and close the buffered stream, so any further attempts to flush/close
880 : // the buffered stream won't cause errors.
881 6 : rv3 = nsBufferedStream::Close();
882 :
883 : // We want to return the errors precisely from Finish()
884 : // and mimick the existing error handling in
885 : // nsBufferedOutputStream::Close() as reference.
886 :
887 6 : if (NS_FAILED(rv1)) {
888 0 : return rv1;
889 : }
890 6 : if (NS_FAILED(rv2)) {
891 0 : return rv2;
892 : }
893 6 : return rv3;
894 : }
895 :
896 : static nsresult
897 0 : nsReadFromInputStream(nsIOutputStream* outStr,
898 : void* closure,
899 : char* toRawSegment,
900 : uint32_t offset,
901 : uint32_t count,
902 : uint32_t *readCount)
903 : {
904 0 : nsIInputStream* fromStream = (nsIInputStream*)closure;
905 0 : return fromStream->Read(toRawSegment, count, readCount);
906 : }
907 :
908 : NS_IMETHODIMP
909 0 : nsBufferedOutputStream::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval)
910 : {
911 0 : return WriteSegments(nsReadFromInputStream, inStr, count, _retval);
912 : }
913 :
914 : NS_IMETHODIMP
915 0 : nsBufferedOutputStream::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval)
916 : {
917 0 : *_retval = 0;
918 : nsresult rv;
919 0 : while (count > 0) {
920 0 : uint32_t left = std::min(count, mBufferSize - mCursor);
921 0 : if (left == 0) {
922 0 : rv = Flush();
923 0 : if (NS_FAILED(rv)) {
924 0 : return (*_retval > 0) ? NS_OK : rv;
925 : }
926 :
927 0 : continue;
928 : }
929 :
930 0 : uint32_t read = 0;
931 0 : rv = reader(this, closure, mBuffer + mCursor, *_retval, left, &read);
932 :
933 0 : if (NS_FAILED(rv)) { // If we have read some data, return ok
934 0 : return (*_retval > 0) ? NS_OK : rv;
935 : }
936 0 : mCursor += read;
937 0 : *_retval += read;
938 0 : count -= read;
939 0 : mFillPoint = std::max(mFillPoint, mCursor);
940 : }
941 0 : return NS_OK;
942 : }
943 :
944 : NS_IMETHODIMP
945 0 : nsBufferedOutputStream::IsNonBlocking(bool *aNonBlocking)
946 : {
947 0 : if (mStream) {
948 0 : return Sink()->IsNonBlocking(aNonBlocking);
949 : }
950 0 : return NS_ERROR_NOT_INITIALIZED;
951 : }
952 :
953 : NS_IMETHODIMP_(char*)
954 0 : nsBufferedOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
955 : {
956 0 : NS_ASSERTION(mGetBufferCount == 0, "nested GetBuffer!");
957 0 : if (mGetBufferCount != 0) {
958 0 : return nullptr;
959 : }
960 :
961 0 : if (mBufferDisabled) {
962 0 : return nullptr;
963 : }
964 :
965 0 : char* buf = mBuffer + mCursor;
966 0 : uint32_t rem = mBufferSize - mCursor;
967 0 : if (rem == 0) {
968 0 : if (NS_FAILED(Flush())) {
969 0 : return nullptr;
970 : }
971 0 : buf = mBuffer + mCursor;
972 0 : rem = mBufferSize - mCursor;
973 : }
974 :
975 0 : uint32_t mod = (NS_PTR_TO_INT32(buf) & aAlignMask);
976 0 : if (mod) {
977 0 : uint32_t pad = aAlignMask + 1 - mod;
978 0 : if (pad > rem) {
979 0 : return nullptr;
980 : }
981 :
982 0 : memset(buf, 0, pad);
983 0 : mCursor += pad;
984 0 : buf += pad;
985 0 : rem -= pad;
986 : }
987 :
988 0 : if (aLength > rem) {
989 0 : return nullptr;
990 : }
991 0 : mGetBufferCount++;
992 0 : return buf;
993 : }
994 :
995 : NS_IMETHODIMP_(void)
996 0 : nsBufferedOutputStream::PutBuffer(char* aBuffer, uint32_t aLength)
997 : {
998 0 : NS_ASSERTION(mGetBufferCount == 1, "stray PutBuffer!");
999 0 : if (--mGetBufferCount != 0) {
1000 0 : return;
1001 : }
1002 :
1003 0 : NS_ASSERTION(mCursor + aLength <= mBufferSize, "PutBuffer botch");
1004 0 : mCursor += aLength;
1005 0 : if (mFillPoint < mCursor) {
1006 0 : mFillPoint = mCursor;
1007 : }
1008 : }
1009 :
1010 : NS_IMETHODIMP
1011 0 : nsBufferedOutputStream::DisableBuffering()
1012 : {
1013 0 : NS_ASSERTION(!mBufferDisabled, "redundant call to DisableBuffering!");
1014 0 : NS_ASSERTION(mGetBufferCount == 0,
1015 : "DisableBuffer call between GetBuffer and PutBuffer!");
1016 0 : if (mGetBufferCount != 0) {
1017 0 : return NS_ERROR_UNEXPECTED;
1018 : }
1019 :
1020 : // Empty the buffer so nsBufferedStream::Tell works.
1021 0 : nsresult rv = Flush();
1022 0 : if (NS_FAILED(rv)) {
1023 0 : return rv;
1024 : }
1025 :
1026 0 : mBufferDisabled = true;
1027 0 : return NS_OK;
1028 : }
1029 :
1030 : NS_IMETHODIMP
1031 0 : nsBufferedOutputStream::EnableBuffering()
1032 : {
1033 0 : NS_ASSERTION(mBufferDisabled, "gratuitous call to EnableBuffering!");
1034 0 : mBufferDisabled = false;
1035 0 : return NS_OK;
1036 : }
1037 :
1038 : NS_IMETHODIMP
1039 0 : nsBufferedOutputStream::GetUnbufferedStream(nsISupports* *aStream)
1040 : {
1041 : // Empty the buffer so subsequent i/o trumps any buffered data.
1042 0 : if (mFillPoint) {
1043 0 : nsresult rv = Flush();
1044 0 : if (NS_FAILED(rv)) {
1045 0 : return rv;
1046 : }
1047 : }
1048 :
1049 0 : *aStream = mStream;
1050 0 : NS_IF_ADDREF(*aStream);
1051 0 : return NS_OK;
1052 : }
1053 :
1054 : NS_IMETHODIMP
1055 0 : nsBufferedOutputStream::GetData(nsIOutputStream **aResult)
1056 : {
1057 0 : nsCOMPtr<nsISupports> stream;
1058 0 : nsBufferedStream::GetData(getter_AddRefs(stream));
1059 0 : nsCOMPtr<nsIOutputStream> outputStream = do_QueryInterface(stream);
1060 0 : *aResult = outputStream.forget().take();
1061 0 : return NS_OK;
1062 : }
1063 : #undef METER
1064 :
1065 : ////////////////////////////////////////////////////////////////////////////////
|