Source code

Revision control

Other Tools

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