netwerk/base/src/nsBufferedStreams.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:003ec3efe1a9
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
42 ////////////////////////////////////////////////////////////////////////////////
43 // nsBufferedStream
44
45 nsBufferedStream::nsBufferedStream()
46 : mBuffer(nullptr),
47 mBufferStartOffset(0),
48 mCursor(0),
49 mFillPoint(0),
50 mStream(nullptr),
51 mBufferDisabled(false),
52 mEOF(false),
53 mGetBufferCount(0)
54 {
55 }
56
57 nsBufferedStream::~nsBufferedStream()
58 {
59 Close();
60 }
61
62 NS_IMPL_ISUPPORTS(nsBufferedStream, nsISeekableStream)
63
64 nsresult
65 nsBufferedStream::Init(nsISupports* stream, uint32_t bufferSize)
66 {
67 NS_ASSERTION(stream, "need to supply a stream");
68 NS_ASSERTION(mStream == nullptr, "already inited");
69 mStream = stream;
70 NS_IF_ADDREF(mStream);
71 mBufferSize = bufferSize;
72 mBufferStartOffset = 0;
73 mCursor = 0;
74 const mozilla::fallible_t fallible = mozilla::fallible_t();
75 mBuffer = new (fallible) char[bufferSize];
76 if (mBuffer == nullptr)
77 return NS_ERROR_OUT_OF_MEMORY;
78 return NS_OK;
79 }
80
81 nsresult
82 nsBufferedStream::Close()
83 {
84 NS_IF_RELEASE(mStream);
85 if (mBuffer) {
86 delete[] mBuffer;
87 mBuffer = nullptr;
88 mBufferSize = 0;
89 mBufferStartOffset = 0;
90 mCursor = 0;
91 mFillPoint = 0;
92 }
93 #ifdef METERING
94 {
95 static FILE *tfp;
96 if (!tfp) {
97 tfp = fopen("/tmp/bufstats", "w");
98 if (tfp)
99 setvbuf(tfp, nullptr, _IOLBF, 0);
100 }
101 if (tfp) {
102 fprintf(tfp, "seeks within buffer: %u\n",
103 bufstats.mSeeksWithinBuffer);
104 fprintf(tfp, "seeks outside buffer: %u\n",
105 bufstats.mSeeksOutsideBuffer);
106 fprintf(tfp, "buffer read on seek: %u\n",
107 bufstats.mBufferReadUponSeek);
108 fprintf(tfp, "buffer unread on seek: %u\n",
109 bufstats.mBufferUnreadUponSeek);
110 fprintf(tfp, "bytes read from buffer: %u\n",
111 bufstats.mBytesReadFromBuffer);
112 for (uint32_t i = 0; i < bufstats.mBigSeekIndex; i++) {
113 fprintf(tfp, "bigseek[%u] = {old: %u, new: %u}\n",
114 i,
115 bufstats.mBigSeek[i].mOldOffset,
116 bufstats.mBigSeek[i].mNewOffset);
117 }
118 }
119 }
120 #endif
121 return NS_OK;
122 }
123
124 NS_IMETHODIMP
125 nsBufferedStream::Seek(int32_t whence, int64_t offset)
126 {
127 if (mStream == nullptr)
128 return NS_BASE_STREAM_CLOSED;
129
130 // If the underlying stream isn't a random access store, then fail early.
131 // We could possibly succeed for the case where the seek position denotes
132 // something that happens to be read into the buffer, but that would make
133 // the failure data-dependent.
134 nsresult rv;
135 nsCOMPtr<nsISeekableStream> ras = do_QueryInterface(mStream, &rv);
136 if (NS_FAILED(rv)) return rv;
137
138 int64_t absPos = 0;
139 switch (whence) {
140 case nsISeekableStream::NS_SEEK_SET:
141 absPos = offset;
142 break;
143 case nsISeekableStream::NS_SEEK_CUR:
144 absPos = mBufferStartOffset;
145 absPos += mCursor;
146 absPos += offset;
147 break;
148 case nsISeekableStream::NS_SEEK_END:
149 absPos = -1;
150 break;
151 default:
152 NS_NOTREACHED("bogus seek whence parameter");
153 return NS_ERROR_UNEXPECTED;
154 }
155
156 // Let mCursor point into the existing buffer if the new position is
157 // between the current cursor and the mFillPoint "fencepost" -- the
158 // client may never get around to a Read or Write after this Seek.
159 // Read and Write worry about flushing and filling in that event.
160 // But if we're at EOF, make sure to pass the seek through to the
161 // underlying stream, because it may have auto-closed itself and
162 // needs to reopen.
163 uint32_t offsetInBuffer = uint32_t(absPos - mBufferStartOffset);
164 if (offsetInBuffer <= mFillPoint && !mEOF) {
165 METER(bufstats.mSeeksWithinBuffer++);
166 mCursor = offsetInBuffer;
167 return NS_OK;
168 }
169
170 METER(bufstats.mSeeksOutsideBuffer++);
171 METER(bufstats.mBufferReadUponSeek += mCursor);
172 METER(bufstats.mBufferUnreadUponSeek += mFillPoint - mCursor);
173 rv = Flush();
174 if (NS_FAILED(rv)) return rv;
175
176 rv = ras->Seek(whence, offset);
177 if (NS_FAILED(rv)) return rv;
178
179 mEOF = false;
180
181 // Recompute whether the offset we're seeking to is in our buffer.
182 // Note that we need to recompute because Flush() might have
183 // changed mBufferStartOffset.
184 offsetInBuffer = uint32_t(absPos - mBufferStartOffset);
185 if (offsetInBuffer <= mFillPoint) {
186 // It's safe to just set mCursor to offsetInBuffer. In particular, we
187 // want to avoid calling Fill() here since we already have the data that
188 // was seeked to and calling Fill() might auto-close our underlying
189 // stream in some cases.
190 mCursor = offsetInBuffer;
191 return NS_OK;
192 }
193
194 METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS)
195 bufstats.mBigSeek[bufstats.mBigSeekIndex].mOldOffset =
196 mBufferStartOffset + int64_t(mCursor));
197 const int64_t minus1 = -1;
198 if (absPos == minus1) {
199 // then we had the SEEK_END case, above
200 int64_t tellPos;
201 rv = ras->Tell(&tellPos);
202 mBufferStartOffset = tellPos;
203 if (NS_FAILED(rv)) return rv;
204 }
205 else {
206 mBufferStartOffset = absPos;
207 }
208 METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS)
209 bufstats.mBigSeek[bufstats.mBigSeekIndex++].mNewOffset =
210 mBufferStartOffset);
211
212 mFillPoint = mCursor = 0;
213 return Fill();
214 }
215
216 NS_IMETHODIMP
217 nsBufferedStream::Tell(int64_t *result)
218 {
219 if (mStream == nullptr)
220 return NS_BASE_STREAM_CLOSED;
221
222 int64_t result64 = mBufferStartOffset;
223 result64 += mCursor;
224 *result = result64;
225 return NS_OK;
226 }
227
228 NS_IMETHODIMP
229 nsBufferedStream::SetEOF()
230 {
231 if (mStream == nullptr)
232 return NS_BASE_STREAM_CLOSED;
233
234 nsresult rv;
235 nsCOMPtr<nsISeekableStream> ras = do_QueryInterface(mStream, &rv);
236 if (NS_FAILED(rv)) return rv;
237
238 rv = ras->SetEOF();
239 if (NS_SUCCEEDED(rv))
240 mEOF = true;
241 return rv;
242 }
243
244 ////////////////////////////////////////////////////////////////////////////////
245 // nsBufferedInputStream
246
247 NS_IMPL_ADDREF_INHERITED(nsBufferedInputStream, nsBufferedStream)
248 NS_IMPL_RELEASE_INHERITED(nsBufferedInputStream, nsBufferedStream)
249
250 NS_IMPL_CLASSINFO(nsBufferedInputStream, nullptr, nsIClassInfo::THREADSAFE,
251 NS_BUFFEREDINPUTSTREAM_CID)
252
253 NS_INTERFACE_MAP_BEGIN(nsBufferedInputStream)
254 NS_INTERFACE_MAP_ENTRY(nsIInputStream)
255 NS_INTERFACE_MAP_ENTRY(nsIBufferedInputStream)
256 NS_INTERFACE_MAP_ENTRY(nsIStreamBufferAccess)
257 NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
258 NS_IMPL_QUERY_CLASSINFO(nsBufferedInputStream)
259 NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream)
260
261 NS_IMPL_CI_INTERFACE_GETTER(nsBufferedInputStream,
262 nsIInputStream,
263 nsIBufferedInputStream,
264 nsISeekableStream,
265 nsIStreamBufferAccess)
266
267 nsresult
268 nsBufferedInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
269 {
270 NS_ENSURE_NO_AGGREGATION(aOuter);
271
272 nsBufferedInputStream* stream = new nsBufferedInputStream();
273 if (stream == nullptr)
274 return NS_ERROR_OUT_OF_MEMORY;
275 NS_ADDREF(stream);
276 nsresult rv = stream->QueryInterface(aIID, aResult);
277 NS_RELEASE(stream);
278 return rv;
279 }
280
281 NS_IMETHODIMP
282 nsBufferedInputStream::Init(nsIInputStream* stream, uint32_t bufferSize)
283 {
284 return nsBufferedStream::Init(stream, bufferSize);
285 }
286
287 NS_IMETHODIMP
288 nsBufferedInputStream::Close()
289 {
290 nsresult rv1 = NS_OK, rv2;
291 if (mStream) {
292 rv1 = Source()->Close();
293 NS_RELEASE(mStream);
294 }
295 rv2 = nsBufferedStream::Close();
296 if (NS_FAILED(rv1)) return rv1;
297 return rv2;
298 }
299
300 NS_IMETHODIMP
301 nsBufferedInputStream::Available(uint64_t *result)
302 {
303 nsresult rv = NS_OK;
304 *result = 0;
305 if (mStream) {
306 rv = Source()->Available(result);
307 }
308 *result += (mFillPoint - mCursor);
309 return rv;
310 }
311
312 NS_IMETHODIMP
313 nsBufferedInputStream::Read(char * buf, uint32_t count, uint32_t *result)
314 {
315 if (mBufferDisabled) {
316 if (!mStream) {
317 *result = 0;
318 return NS_OK;
319 }
320 nsresult rv = Source()->Read(buf, count, result);
321 if (NS_SUCCEEDED(rv)) {
322 mBufferStartOffset += *result; // so nsBufferedStream::Tell works
323 if (*result == 0) {
324 mEOF = true;
325 }
326 }
327 return rv;
328 }
329
330 return ReadSegments(NS_CopySegmentToBuffer, buf, count, result);
331 }
332
333 NS_IMETHODIMP
334 nsBufferedInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure,
335 uint32_t count, uint32_t *result)
336 {
337 *result = 0;
338
339 if (!mStream)
340 return NS_OK;
341
342 nsresult rv = NS_OK;
343 while (count > 0) {
344 uint32_t amt = std::min(count, mFillPoint - mCursor);
345 if (amt > 0) {
346 uint32_t read = 0;
347 rv = writer(this, closure, mBuffer + mCursor, *result, amt, &read);
348 if (NS_FAILED(rv)) {
349 // errors returned from the writer end here!
350 rv = NS_OK;
351 break;
352 }
353 *result += read;
354 count -= read;
355 mCursor += read;
356 }
357 else {
358 rv = Fill();
359 if (NS_FAILED(rv) || mFillPoint == mCursor)
360 break;
361 }
362 }
363 return (*result > 0) ? NS_OK : rv;
364 }
365
366 NS_IMETHODIMP
367 nsBufferedInputStream::IsNonBlocking(bool *aNonBlocking)
368 {
369 if (mStream)
370 return Source()->IsNonBlocking(aNonBlocking);
371 return NS_ERROR_NOT_INITIALIZED;
372 }
373
374 NS_IMETHODIMP
375 nsBufferedInputStream::Fill()
376 {
377 if (mBufferDisabled)
378 return NS_OK;
379 NS_ENSURE_TRUE(mStream, NS_ERROR_NOT_INITIALIZED);
380
381 nsresult rv;
382 int32_t rem = int32_t(mFillPoint - mCursor);
383 if (rem > 0) {
384 // slide the remainder down to the start of the buffer
385 // |<------------->|<--rem-->|<--->|
386 // b c f s
387 memcpy(mBuffer, mBuffer + mCursor, rem);
388 }
389 mBufferStartOffset += mCursor;
390 mFillPoint = rem;
391 mCursor = 0;
392
393 uint32_t amt;
394 rv = Source()->Read(mBuffer + mFillPoint, mBufferSize - mFillPoint, &amt);
395 if (NS_FAILED(rv)) return rv;
396
397 if (amt == 0)
398 mEOF = true;
399
400 mFillPoint += amt;
401 return NS_OK;
402 }
403
404 NS_IMETHODIMP_(char*)
405 nsBufferedInputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
406 {
407 NS_ASSERTION(mGetBufferCount == 0, "nested GetBuffer!");
408 if (mGetBufferCount != 0)
409 return nullptr;
410
411 if (mBufferDisabled)
412 return nullptr;
413
414 char* buf = mBuffer + mCursor;
415 uint32_t rem = mFillPoint - mCursor;
416 if (rem == 0) {
417 if (NS_FAILED(Fill()))
418 return nullptr;
419 buf = mBuffer + mCursor;
420 rem = mFillPoint - mCursor;
421 }
422
423 uint32_t mod = (NS_PTR_TO_INT32(buf) & aAlignMask);
424 if (mod) {
425 uint32_t pad = aAlignMask + 1 - mod;
426 if (pad > rem)
427 return nullptr;
428
429 memset(buf, 0, pad);
430 mCursor += pad;
431 buf += pad;
432 rem -= pad;
433 }
434
435 if (aLength > rem)
436 return nullptr;
437 mGetBufferCount++;
438 return buf;
439 }
440
441 NS_IMETHODIMP_(void)
442 nsBufferedInputStream::PutBuffer(char* aBuffer, uint32_t aLength)
443 {
444 NS_ASSERTION(mGetBufferCount == 1, "stray PutBuffer!");
445 if (--mGetBufferCount != 0)
446 return;
447
448 NS_ASSERTION(mCursor + aLength <= mFillPoint, "PutBuffer botch");
449 mCursor += aLength;
450 }
451
452 NS_IMETHODIMP
453 nsBufferedInputStream::DisableBuffering()
454 {
455 NS_ASSERTION(!mBufferDisabled, "redundant call to DisableBuffering!");
456 NS_ASSERTION(mGetBufferCount == 0,
457 "DisableBuffer call between GetBuffer and PutBuffer!");
458 if (mGetBufferCount != 0)
459 return NS_ERROR_UNEXPECTED;
460
461 // Empty the buffer so nsBufferedStream::Tell works.
462 mBufferStartOffset += mCursor;
463 mFillPoint = mCursor = 0;
464 mBufferDisabled = true;
465 return NS_OK;
466 }
467
468 NS_IMETHODIMP
469 nsBufferedInputStream::EnableBuffering()
470 {
471 NS_ASSERTION(mBufferDisabled, "gratuitous call to EnableBuffering!");
472 mBufferDisabled = false;
473 return NS_OK;
474 }
475
476 NS_IMETHODIMP
477 nsBufferedInputStream::GetUnbufferedStream(nsISupports* *aStream)
478 {
479 // Empty the buffer so subsequent i/o trumps any buffered data.
480 mBufferStartOffset += mCursor;
481 mFillPoint = mCursor = 0;
482
483 *aStream = mStream;
484 NS_IF_ADDREF(*aStream);
485 return NS_OK;
486 }
487
488 void
489 nsBufferedInputStream::Serialize(InputStreamParams& aParams,
490 FileDescriptorArray& aFileDescriptors)
491 {
492 BufferedInputStreamParams params;
493
494 if (mStream) {
495 nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream);
496 MOZ_ASSERT(stream);
497
498 InputStreamParams wrappedParams;
499 SerializeInputStream(stream, wrappedParams, aFileDescriptors);
500
501 params.optionalStream() = wrappedParams;
502 }
503 else {
504 params.optionalStream() = mozilla::void_t();
505 }
506
507 params.bufferSize() = mBufferSize;
508
509 aParams = params;
510 }
511
512 bool
513 nsBufferedInputStream::Deserialize(const InputStreamParams& aParams,
514 const FileDescriptorArray& aFileDescriptors)
515 {
516 if (aParams.type() != InputStreamParams::TBufferedInputStreamParams) {
517 NS_ERROR("Received unknown parameters from the other process!");
518 return false;
519 }
520
521 const BufferedInputStreamParams& params =
522 aParams.get_BufferedInputStreamParams();
523 const OptionalInputStreamParams& wrappedParams = params.optionalStream();
524
525 nsCOMPtr<nsIInputStream> stream;
526 if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) {
527 stream = DeserializeInputStream(wrappedParams.get_InputStreamParams(),
528 aFileDescriptors);
529 if (!stream) {
530 NS_WARNING("Failed to deserialize wrapped stream!");
531 return false;
532 }
533 }
534 else {
535 NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t,
536 "Unknown type for OptionalInputStreamParams!");
537 }
538
539 nsresult rv = Init(stream, params.bufferSize());
540 NS_ENSURE_SUCCESS(rv, false);
541
542 return true;
543 }
544
545 ////////////////////////////////////////////////////////////////////////////////
546 // nsBufferedOutputStream
547
548 NS_IMPL_ADDREF_INHERITED(nsBufferedOutputStream, nsBufferedStream)
549 NS_IMPL_RELEASE_INHERITED(nsBufferedOutputStream, nsBufferedStream)
550 // This QI uses NS_INTERFACE_MAP_ENTRY_CONDITIONAL to check for
551 // non-nullness of mSafeStream.
552 NS_INTERFACE_MAP_BEGIN(nsBufferedOutputStream)
553 NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
554 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISafeOutputStream, mSafeStream)
555 NS_INTERFACE_MAP_ENTRY(nsIBufferedOutputStream)
556 NS_INTERFACE_MAP_ENTRY(nsIStreamBufferAccess)
557 NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream)
558
559 nsresult
560 nsBufferedOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
561 {
562 NS_ENSURE_NO_AGGREGATION(aOuter);
563
564 nsBufferedOutputStream* stream = new nsBufferedOutputStream();
565 if (stream == nullptr)
566 return NS_ERROR_OUT_OF_MEMORY;
567 NS_ADDREF(stream);
568 nsresult rv = stream->QueryInterface(aIID, aResult);
569 NS_RELEASE(stream);
570 return rv;
571 }
572
573 NS_IMETHODIMP
574 nsBufferedOutputStream::Init(nsIOutputStream* stream, uint32_t bufferSize)
575 {
576 // QI stream to an nsISafeOutputStream, to see if we should support it
577 mSafeStream = do_QueryInterface(stream);
578
579 return nsBufferedStream::Init(stream, bufferSize);
580 }
581
582 NS_IMETHODIMP
583 nsBufferedOutputStream::Close()
584 {
585 nsresult rv1, rv2 = NS_OK, rv3;
586 rv1 = Flush();
587 // If we fail to Flush all the data, then we close anyway and drop the
588 // remaining data in the buffer. We do this because it's what Unix does
589 // for fclose and close. However, we report the error from Flush anyway.
590 if (mStream) {
591 rv2 = Sink()->Close();
592 NS_RELEASE(mStream);
593 }
594 rv3 = nsBufferedStream::Close();
595 if (NS_FAILED(rv1)) return rv1;
596 if (NS_FAILED(rv2)) return rv2;
597 return rv3;
598 }
599
600 NS_IMETHODIMP
601 nsBufferedOutputStream::Write(const char *buf, uint32_t count, uint32_t *result)
602 {
603 nsresult rv = NS_OK;
604 uint32_t written = 0;
605 while (count > 0) {
606 uint32_t amt = std::min(count, mBufferSize - mCursor);
607 if (amt > 0) {
608 memcpy(mBuffer + mCursor, buf + written, amt);
609 written += amt;
610 count -= amt;
611 mCursor += amt;
612 if (mFillPoint < mCursor)
613 mFillPoint = mCursor;
614 }
615 else {
616 NS_ASSERTION(mFillPoint, "iloop in nsBufferedOutputStream::Write!");
617 rv = Flush();
618 if (NS_FAILED(rv)) break;
619 }
620 }
621 *result = written;
622 return (written > 0) ? NS_OK : rv;
623 }
624
625 NS_IMETHODIMP
626 nsBufferedOutputStream::Flush()
627 {
628 nsresult rv;
629 uint32_t amt;
630 if (!mStream) {
631 // Stream already cancelled/flushed; probably because of previous error.
632 return NS_OK;
633 }
634 rv = Sink()->Write(mBuffer, mFillPoint, &amt);
635 if (NS_FAILED(rv)) return rv;
636 mBufferStartOffset += amt;
637 if (amt == mFillPoint) {
638 mFillPoint = mCursor = 0;
639 return NS_OK; // flushed everything
640 }
641
642 // slide the remainder down to the start of the buffer
643 // |<-------------->|<---|----->|
644 // b a c s
645 uint32_t rem = mFillPoint - amt;
646 memcpy(mBuffer, mBuffer + amt, rem);
647 mFillPoint = mCursor = rem;
648 return NS_ERROR_FAILURE; // didn't flush all
649 }
650
651 // nsISafeOutputStream
652 NS_IMETHODIMP
653 nsBufferedOutputStream::Finish()
654 {
655 // flush the stream, to write out any buffered data...
656 nsresult rv = nsBufferedOutputStream::Flush();
657 if (NS_FAILED(rv))
658 NS_WARNING("failed to flush buffered data! possible dataloss");
659
660 // ... and finish the underlying stream...
661 if (NS_SUCCEEDED(rv))
662 rv = mSafeStream->Finish();
663 else
664 Sink()->Close();
665
666 // ... and close the buffered stream, so any further attempts to flush/close
667 // the buffered stream won't cause errors.
668 nsBufferedStream::Close();
669
670 return rv;
671 }
672
673 static NS_METHOD
674 nsReadFromInputStream(nsIOutputStream* outStr,
675 void* closure,
676 char* toRawSegment,
677 uint32_t offset,
678 uint32_t count,
679 uint32_t *readCount)
680 {
681 nsIInputStream* fromStream = (nsIInputStream*)closure;
682 return fromStream->Read(toRawSegment, count, readCount);
683 }
684
685 NS_IMETHODIMP
686 nsBufferedOutputStream::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval)
687 {
688 return WriteSegments(nsReadFromInputStream, inStr, count, _retval);
689 }
690
691 NS_IMETHODIMP
692 nsBufferedOutputStream::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval)
693 {
694 *_retval = 0;
695 nsresult rv;
696 while (count > 0) {
697 uint32_t left = std::min(count, mBufferSize - mCursor);
698 if (left == 0) {
699 rv = Flush();
700 if (NS_FAILED(rv))
701 return rv;
702
703 continue;
704 }
705
706 uint32_t read = 0;
707 rv = reader(this, closure, mBuffer + mCursor, *_retval, left, &read);
708
709 if (NS_FAILED(rv)) // If we have written some data, return ok
710 return (*_retval > 0) ? NS_OK : rv;
711 mCursor += read;
712 *_retval += read;
713 count -= read;
714 mFillPoint = std::max(mFillPoint, mCursor);
715 }
716 return NS_OK;
717 }
718
719 NS_IMETHODIMP
720 nsBufferedOutputStream::IsNonBlocking(bool *aNonBlocking)
721 {
722 if (mStream)
723 return Sink()->IsNonBlocking(aNonBlocking);
724 return NS_ERROR_NOT_INITIALIZED;
725 }
726
727 NS_IMETHODIMP_(char*)
728 nsBufferedOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
729 {
730 NS_ASSERTION(mGetBufferCount == 0, "nested GetBuffer!");
731 if (mGetBufferCount != 0)
732 return nullptr;
733
734 if (mBufferDisabled)
735 return nullptr;
736
737 char* buf = mBuffer + mCursor;
738 uint32_t rem = mBufferSize - mCursor;
739 if (rem == 0) {
740 if (NS_FAILED(Flush()))
741 return nullptr;
742 buf = mBuffer + mCursor;
743 rem = mBufferSize - mCursor;
744 }
745
746 uint32_t mod = (NS_PTR_TO_INT32(buf) & aAlignMask);
747 if (mod) {
748 uint32_t pad = aAlignMask + 1 - mod;
749 if (pad > rem)
750 return nullptr;
751
752 memset(buf, 0, pad);
753 mCursor += pad;
754 buf += pad;
755 rem -= pad;
756 }
757
758 if (aLength > rem)
759 return nullptr;
760 mGetBufferCount++;
761 return buf;
762 }
763
764 NS_IMETHODIMP_(void)
765 nsBufferedOutputStream::PutBuffer(char* aBuffer, uint32_t aLength)
766 {
767 NS_ASSERTION(mGetBufferCount == 1, "stray PutBuffer!");
768 if (--mGetBufferCount != 0)
769 return;
770
771 NS_ASSERTION(mCursor + aLength <= mBufferSize, "PutBuffer botch");
772 mCursor += aLength;
773 if (mFillPoint < mCursor)
774 mFillPoint = mCursor;
775 }
776
777 NS_IMETHODIMP
778 nsBufferedOutputStream::DisableBuffering()
779 {
780 NS_ASSERTION(!mBufferDisabled, "redundant call to DisableBuffering!");
781 NS_ASSERTION(mGetBufferCount == 0,
782 "DisableBuffer call between GetBuffer and PutBuffer!");
783 if (mGetBufferCount != 0)
784 return NS_ERROR_UNEXPECTED;
785
786 // Empty the buffer so nsBufferedStream::Tell works.
787 nsresult rv = Flush();
788 if (NS_FAILED(rv))
789 return rv;
790
791 mBufferDisabled = true;
792 return NS_OK;
793 }
794
795 NS_IMETHODIMP
796 nsBufferedOutputStream::EnableBuffering()
797 {
798 NS_ASSERTION(mBufferDisabled, "gratuitous call to EnableBuffering!");
799 mBufferDisabled = false;
800 return NS_OK;
801 }
802
803 NS_IMETHODIMP
804 nsBufferedOutputStream::GetUnbufferedStream(nsISupports* *aStream)
805 {
806 // Empty the buffer so subsequent i/o trumps any buffered data.
807 if (mFillPoint) {
808 nsresult rv = Flush();
809 if (NS_FAILED(rv))
810 return rv;
811 }
812
813 *aStream = mStream;
814 NS_IF_ADDREF(*aStream);
815 return NS_OK;
816 }
817
818 ////////////////////////////////////////////////////////////////////////////////

mercurial