Coverage Report

Created: 2018-09-25 14:53

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