netwerk/base/src/nsBufferedStreams.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial