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