1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/base/src/nsBufferedStreams.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,818 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "ipc/IPCMessageUtils.h" 1.10 + 1.11 +#include "nsBufferedStreams.h" 1.12 +#include "nsStreamUtils.h" 1.13 +#include "nsNetCID.h" 1.14 +#include "nsIClassInfoImpl.h" 1.15 +#include "mozilla/ipc/InputStreamUtils.h" 1.16 +#include <algorithm> 1.17 + 1.18 +#ifdef DEBUG_brendan 1.19 +# define METERING 1.20 +#endif 1.21 + 1.22 +#ifdef METERING 1.23 +# include <stdio.h> 1.24 +# define METER(x) x 1.25 +# define MAX_BIG_SEEKS 20 1.26 + 1.27 +static struct { 1.28 + uint32_t mSeeksWithinBuffer; 1.29 + uint32_t mSeeksOutsideBuffer; 1.30 + uint32_t mBufferReadUponSeek; 1.31 + uint32_t mBufferUnreadUponSeek; 1.32 + uint32_t mBytesReadFromBuffer; 1.33 + uint32_t mBigSeekIndex; 1.34 + struct { 1.35 + int64_t mOldOffset; 1.36 + int64_t mNewOffset; 1.37 + } mBigSeek[MAX_BIG_SEEKS]; 1.38 +} bufstats; 1.39 +#else 1.40 +# define METER(x) /* nothing */ 1.41 +#endif 1.42 + 1.43 +using namespace mozilla::ipc; 1.44 + 1.45 +//////////////////////////////////////////////////////////////////////////////// 1.46 +// nsBufferedStream 1.47 + 1.48 +nsBufferedStream::nsBufferedStream() 1.49 + : mBuffer(nullptr), 1.50 + mBufferStartOffset(0), 1.51 + mCursor(0), 1.52 + mFillPoint(0), 1.53 + mStream(nullptr), 1.54 + mBufferDisabled(false), 1.55 + mEOF(false), 1.56 + mGetBufferCount(0) 1.57 +{ 1.58 +} 1.59 + 1.60 +nsBufferedStream::~nsBufferedStream() 1.61 +{ 1.62 + Close(); 1.63 +} 1.64 + 1.65 +NS_IMPL_ISUPPORTS(nsBufferedStream, nsISeekableStream) 1.66 + 1.67 +nsresult 1.68 +nsBufferedStream::Init(nsISupports* stream, uint32_t bufferSize) 1.69 +{ 1.70 + NS_ASSERTION(stream, "need to supply a stream"); 1.71 + NS_ASSERTION(mStream == nullptr, "already inited"); 1.72 + mStream = stream; 1.73 + NS_IF_ADDREF(mStream); 1.74 + mBufferSize = bufferSize; 1.75 + mBufferStartOffset = 0; 1.76 + mCursor = 0; 1.77 + const mozilla::fallible_t fallible = mozilla::fallible_t(); 1.78 + mBuffer = new (fallible) char[bufferSize]; 1.79 + if (mBuffer == nullptr) 1.80 + return NS_ERROR_OUT_OF_MEMORY; 1.81 + return NS_OK; 1.82 +} 1.83 + 1.84 +nsresult 1.85 +nsBufferedStream::Close() 1.86 +{ 1.87 + NS_IF_RELEASE(mStream); 1.88 + if (mBuffer) { 1.89 + delete[] mBuffer; 1.90 + mBuffer = nullptr; 1.91 + mBufferSize = 0; 1.92 + mBufferStartOffset = 0; 1.93 + mCursor = 0; 1.94 + mFillPoint = 0; 1.95 + } 1.96 +#ifdef METERING 1.97 + { 1.98 + static FILE *tfp; 1.99 + if (!tfp) { 1.100 + tfp = fopen("/tmp/bufstats", "w"); 1.101 + if (tfp) 1.102 + setvbuf(tfp, nullptr, _IOLBF, 0); 1.103 + } 1.104 + if (tfp) { 1.105 + fprintf(tfp, "seeks within buffer: %u\n", 1.106 + bufstats.mSeeksWithinBuffer); 1.107 + fprintf(tfp, "seeks outside buffer: %u\n", 1.108 + bufstats.mSeeksOutsideBuffer); 1.109 + fprintf(tfp, "buffer read on seek: %u\n", 1.110 + bufstats.mBufferReadUponSeek); 1.111 + fprintf(tfp, "buffer unread on seek: %u\n", 1.112 + bufstats.mBufferUnreadUponSeek); 1.113 + fprintf(tfp, "bytes read from buffer: %u\n", 1.114 + bufstats.mBytesReadFromBuffer); 1.115 + for (uint32_t i = 0; i < bufstats.mBigSeekIndex; i++) { 1.116 + fprintf(tfp, "bigseek[%u] = {old: %u, new: %u}\n", 1.117 + i, 1.118 + bufstats.mBigSeek[i].mOldOffset, 1.119 + bufstats.mBigSeek[i].mNewOffset); 1.120 + } 1.121 + } 1.122 + } 1.123 +#endif 1.124 + return NS_OK; 1.125 +} 1.126 + 1.127 +NS_IMETHODIMP 1.128 +nsBufferedStream::Seek(int32_t whence, int64_t offset) 1.129 +{ 1.130 + if (mStream == nullptr) 1.131 + return NS_BASE_STREAM_CLOSED; 1.132 + 1.133 + // If the underlying stream isn't a random access store, then fail early. 1.134 + // We could possibly succeed for the case where the seek position denotes 1.135 + // something that happens to be read into the buffer, but that would make 1.136 + // the failure data-dependent. 1.137 + nsresult rv; 1.138 + nsCOMPtr<nsISeekableStream> ras = do_QueryInterface(mStream, &rv); 1.139 + if (NS_FAILED(rv)) return rv; 1.140 + 1.141 + int64_t absPos = 0; 1.142 + switch (whence) { 1.143 + case nsISeekableStream::NS_SEEK_SET: 1.144 + absPos = offset; 1.145 + break; 1.146 + case nsISeekableStream::NS_SEEK_CUR: 1.147 + absPos = mBufferStartOffset; 1.148 + absPos += mCursor; 1.149 + absPos += offset; 1.150 + break; 1.151 + case nsISeekableStream::NS_SEEK_END: 1.152 + absPos = -1; 1.153 + break; 1.154 + default: 1.155 + NS_NOTREACHED("bogus seek whence parameter"); 1.156 + return NS_ERROR_UNEXPECTED; 1.157 + } 1.158 + 1.159 + // Let mCursor point into the existing buffer if the new position is 1.160 + // between the current cursor and the mFillPoint "fencepost" -- the 1.161 + // client may never get around to a Read or Write after this Seek. 1.162 + // Read and Write worry about flushing and filling in that event. 1.163 + // But if we're at EOF, make sure to pass the seek through to the 1.164 + // underlying stream, because it may have auto-closed itself and 1.165 + // needs to reopen. 1.166 + uint32_t offsetInBuffer = uint32_t(absPos - mBufferStartOffset); 1.167 + if (offsetInBuffer <= mFillPoint && !mEOF) { 1.168 + METER(bufstats.mSeeksWithinBuffer++); 1.169 + mCursor = offsetInBuffer; 1.170 + return NS_OK; 1.171 + } 1.172 + 1.173 + METER(bufstats.mSeeksOutsideBuffer++); 1.174 + METER(bufstats.mBufferReadUponSeek += mCursor); 1.175 + METER(bufstats.mBufferUnreadUponSeek += mFillPoint - mCursor); 1.176 + rv = Flush(); 1.177 + if (NS_FAILED(rv)) return rv; 1.178 + 1.179 + rv = ras->Seek(whence, offset); 1.180 + if (NS_FAILED(rv)) return rv; 1.181 + 1.182 + mEOF = false; 1.183 + 1.184 + // Recompute whether the offset we're seeking to is in our buffer. 1.185 + // Note that we need to recompute because Flush() might have 1.186 + // changed mBufferStartOffset. 1.187 + offsetInBuffer = uint32_t(absPos - mBufferStartOffset); 1.188 + if (offsetInBuffer <= mFillPoint) { 1.189 + // It's safe to just set mCursor to offsetInBuffer. In particular, we 1.190 + // want to avoid calling Fill() here since we already have the data that 1.191 + // was seeked to and calling Fill() might auto-close our underlying 1.192 + // stream in some cases. 1.193 + mCursor = offsetInBuffer; 1.194 + return NS_OK; 1.195 + } 1.196 + 1.197 + METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS) 1.198 + bufstats.mBigSeek[bufstats.mBigSeekIndex].mOldOffset = 1.199 + mBufferStartOffset + int64_t(mCursor)); 1.200 + const int64_t minus1 = -1; 1.201 + if (absPos == minus1) { 1.202 + // then we had the SEEK_END case, above 1.203 + int64_t tellPos; 1.204 + rv = ras->Tell(&tellPos); 1.205 + mBufferStartOffset = tellPos; 1.206 + if (NS_FAILED(rv)) return rv; 1.207 + } 1.208 + else { 1.209 + mBufferStartOffset = absPos; 1.210 + } 1.211 + METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS) 1.212 + bufstats.mBigSeek[bufstats.mBigSeekIndex++].mNewOffset = 1.213 + mBufferStartOffset); 1.214 + 1.215 + mFillPoint = mCursor = 0; 1.216 + return Fill(); 1.217 +} 1.218 + 1.219 +NS_IMETHODIMP 1.220 +nsBufferedStream::Tell(int64_t *result) 1.221 +{ 1.222 + if (mStream == nullptr) 1.223 + return NS_BASE_STREAM_CLOSED; 1.224 + 1.225 + int64_t result64 = mBufferStartOffset; 1.226 + result64 += mCursor; 1.227 + *result = result64; 1.228 + return NS_OK; 1.229 +} 1.230 + 1.231 +NS_IMETHODIMP 1.232 +nsBufferedStream::SetEOF() 1.233 +{ 1.234 + if (mStream == nullptr) 1.235 + return NS_BASE_STREAM_CLOSED; 1.236 + 1.237 + nsresult rv; 1.238 + nsCOMPtr<nsISeekableStream> ras = do_QueryInterface(mStream, &rv); 1.239 + if (NS_FAILED(rv)) return rv; 1.240 + 1.241 + rv = ras->SetEOF(); 1.242 + if (NS_SUCCEEDED(rv)) 1.243 + mEOF = true; 1.244 + return rv; 1.245 +} 1.246 + 1.247 +//////////////////////////////////////////////////////////////////////////////// 1.248 +// nsBufferedInputStream 1.249 + 1.250 +NS_IMPL_ADDREF_INHERITED(nsBufferedInputStream, nsBufferedStream) 1.251 +NS_IMPL_RELEASE_INHERITED(nsBufferedInputStream, nsBufferedStream) 1.252 + 1.253 +NS_IMPL_CLASSINFO(nsBufferedInputStream, nullptr, nsIClassInfo::THREADSAFE, 1.254 + NS_BUFFEREDINPUTSTREAM_CID) 1.255 + 1.256 +NS_INTERFACE_MAP_BEGIN(nsBufferedInputStream) 1.257 + NS_INTERFACE_MAP_ENTRY(nsIInputStream) 1.258 + NS_INTERFACE_MAP_ENTRY(nsIBufferedInputStream) 1.259 + NS_INTERFACE_MAP_ENTRY(nsIStreamBufferAccess) 1.260 + NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream) 1.261 + NS_IMPL_QUERY_CLASSINFO(nsBufferedInputStream) 1.262 +NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream) 1.263 + 1.264 +NS_IMPL_CI_INTERFACE_GETTER(nsBufferedInputStream, 1.265 + nsIInputStream, 1.266 + nsIBufferedInputStream, 1.267 + nsISeekableStream, 1.268 + nsIStreamBufferAccess) 1.269 + 1.270 +nsresult 1.271 +nsBufferedInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) 1.272 +{ 1.273 + NS_ENSURE_NO_AGGREGATION(aOuter); 1.274 + 1.275 + nsBufferedInputStream* stream = new nsBufferedInputStream(); 1.276 + if (stream == nullptr) 1.277 + return NS_ERROR_OUT_OF_MEMORY; 1.278 + NS_ADDREF(stream); 1.279 + nsresult rv = stream->QueryInterface(aIID, aResult); 1.280 + NS_RELEASE(stream); 1.281 + return rv; 1.282 +} 1.283 + 1.284 +NS_IMETHODIMP 1.285 +nsBufferedInputStream::Init(nsIInputStream* stream, uint32_t bufferSize) 1.286 +{ 1.287 + return nsBufferedStream::Init(stream, bufferSize); 1.288 +} 1.289 + 1.290 +NS_IMETHODIMP 1.291 +nsBufferedInputStream::Close() 1.292 +{ 1.293 + nsresult rv1 = NS_OK, rv2; 1.294 + if (mStream) { 1.295 + rv1 = Source()->Close(); 1.296 + NS_RELEASE(mStream); 1.297 + } 1.298 + rv2 = nsBufferedStream::Close(); 1.299 + if (NS_FAILED(rv1)) return rv1; 1.300 + return rv2; 1.301 +} 1.302 + 1.303 +NS_IMETHODIMP 1.304 +nsBufferedInputStream::Available(uint64_t *result) 1.305 +{ 1.306 + nsresult rv = NS_OK; 1.307 + *result = 0; 1.308 + if (mStream) { 1.309 + rv = Source()->Available(result); 1.310 + } 1.311 + *result += (mFillPoint - mCursor); 1.312 + return rv; 1.313 +} 1.314 + 1.315 +NS_IMETHODIMP 1.316 +nsBufferedInputStream::Read(char * buf, uint32_t count, uint32_t *result) 1.317 +{ 1.318 + if (mBufferDisabled) { 1.319 + if (!mStream) { 1.320 + *result = 0; 1.321 + return NS_OK; 1.322 + } 1.323 + nsresult rv = Source()->Read(buf, count, result); 1.324 + if (NS_SUCCEEDED(rv)) { 1.325 + mBufferStartOffset += *result; // so nsBufferedStream::Tell works 1.326 + if (*result == 0) { 1.327 + mEOF = true; 1.328 + } 1.329 + } 1.330 + return rv; 1.331 + } 1.332 + 1.333 + return ReadSegments(NS_CopySegmentToBuffer, buf, count, result); 1.334 +} 1.335 + 1.336 +NS_IMETHODIMP 1.337 +nsBufferedInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure, 1.338 + uint32_t count, uint32_t *result) 1.339 +{ 1.340 + *result = 0; 1.341 + 1.342 + if (!mStream) 1.343 + return NS_OK; 1.344 + 1.345 + nsresult rv = NS_OK; 1.346 + while (count > 0) { 1.347 + uint32_t amt = std::min(count, mFillPoint - mCursor); 1.348 + if (amt > 0) { 1.349 + uint32_t read = 0; 1.350 + rv = writer(this, closure, mBuffer + mCursor, *result, amt, &read); 1.351 + if (NS_FAILED(rv)) { 1.352 + // errors returned from the writer end here! 1.353 + rv = NS_OK; 1.354 + break; 1.355 + } 1.356 + *result += read; 1.357 + count -= read; 1.358 + mCursor += read; 1.359 + } 1.360 + else { 1.361 + rv = Fill(); 1.362 + if (NS_FAILED(rv) || mFillPoint == mCursor) 1.363 + break; 1.364 + } 1.365 + } 1.366 + return (*result > 0) ? NS_OK : rv; 1.367 +} 1.368 + 1.369 +NS_IMETHODIMP 1.370 +nsBufferedInputStream::IsNonBlocking(bool *aNonBlocking) 1.371 +{ 1.372 + if (mStream) 1.373 + return Source()->IsNonBlocking(aNonBlocking); 1.374 + return NS_ERROR_NOT_INITIALIZED; 1.375 +} 1.376 + 1.377 +NS_IMETHODIMP 1.378 +nsBufferedInputStream::Fill() 1.379 +{ 1.380 + if (mBufferDisabled) 1.381 + return NS_OK; 1.382 + NS_ENSURE_TRUE(mStream, NS_ERROR_NOT_INITIALIZED); 1.383 + 1.384 + nsresult rv; 1.385 + int32_t rem = int32_t(mFillPoint - mCursor); 1.386 + if (rem > 0) { 1.387 + // slide the remainder down to the start of the buffer 1.388 + // |<------------->|<--rem-->|<--->| 1.389 + // b c f s 1.390 + memcpy(mBuffer, mBuffer + mCursor, rem); 1.391 + } 1.392 + mBufferStartOffset += mCursor; 1.393 + mFillPoint = rem; 1.394 + mCursor = 0; 1.395 + 1.396 + uint32_t amt; 1.397 + rv = Source()->Read(mBuffer + mFillPoint, mBufferSize - mFillPoint, &amt); 1.398 + if (NS_FAILED(rv)) return rv; 1.399 + 1.400 + if (amt == 0) 1.401 + mEOF = true; 1.402 + 1.403 + mFillPoint += amt; 1.404 + return NS_OK; 1.405 +} 1.406 + 1.407 +NS_IMETHODIMP_(char*) 1.408 +nsBufferedInputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask) 1.409 +{ 1.410 + NS_ASSERTION(mGetBufferCount == 0, "nested GetBuffer!"); 1.411 + if (mGetBufferCount != 0) 1.412 + return nullptr; 1.413 + 1.414 + if (mBufferDisabled) 1.415 + return nullptr; 1.416 + 1.417 + char* buf = mBuffer + mCursor; 1.418 + uint32_t rem = mFillPoint - mCursor; 1.419 + if (rem == 0) { 1.420 + if (NS_FAILED(Fill())) 1.421 + return nullptr; 1.422 + buf = mBuffer + mCursor; 1.423 + rem = mFillPoint - mCursor; 1.424 + } 1.425 + 1.426 + uint32_t mod = (NS_PTR_TO_INT32(buf) & aAlignMask); 1.427 + if (mod) { 1.428 + uint32_t pad = aAlignMask + 1 - mod; 1.429 + if (pad > rem) 1.430 + return nullptr; 1.431 + 1.432 + memset(buf, 0, pad); 1.433 + mCursor += pad; 1.434 + buf += pad; 1.435 + rem -= pad; 1.436 + } 1.437 + 1.438 + if (aLength > rem) 1.439 + return nullptr; 1.440 + mGetBufferCount++; 1.441 + return buf; 1.442 +} 1.443 + 1.444 +NS_IMETHODIMP_(void) 1.445 +nsBufferedInputStream::PutBuffer(char* aBuffer, uint32_t aLength) 1.446 +{ 1.447 + NS_ASSERTION(mGetBufferCount == 1, "stray PutBuffer!"); 1.448 + if (--mGetBufferCount != 0) 1.449 + return; 1.450 + 1.451 + NS_ASSERTION(mCursor + aLength <= mFillPoint, "PutBuffer botch"); 1.452 + mCursor += aLength; 1.453 +} 1.454 + 1.455 +NS_IMETHODIMP 1.456 +nsBufferedInputStream::DisableBuffering() 1.457 +{ 1.458 + NS_ASSERTION(!mBufferDisabled, "redundant call to DisableBuffering!"); 1.459 + NS_ASSERTION(mGetBufferCount == 0, 1.460 + "DisableBuffer call between GetBuffer and PutBuffer!"); 1.461 + if (mGetBufferCount != 0) 1.462 + return NS_ERROR_UNEXPECTED; 1.463 + 1.464 + // Empty the buffer so nsBufferedStream::Tell works. 1.465 + mBufferStartOffset += mCursor; 1.466 + mFillPoint = mCursor = 0; 1.467 + mBufferDisabled = true; 1.468 + return NS_OK; 1.469 +} 1.470 + 1.471 +NS_IMETHODIMP 1.472 +nsBufferedInputStream::EnableBuffering() 1.473 +{ 1.474 + NS_ASSERTION(mBufferDisabled, "gratuitous call to EnableBuffering!"); 1.475 + mBufferDisabled = false; 1.476 + return NS_OK; 1.477 +} 1.478 + 1.479 +NS_IMETHODIMP 1.480 +nsBufferedInputStream::GetUnbufferedStream(nsISupports* *aStream) 1.481 +{ 1.482 + // Empty the buffer so subsequent i/o trumps any buffered data. 1.483 + mBufferStartOffset += mCursor; 1.484 + mFillPoint = mCursor = 0; 1.485 + 1.486 + *aStream = mStream; 1.487 + NS_IF_ADDREF(*aStream); 1.488 + return NS_OK; 1.489 +} 1.490 + 1.491 +void 1.492 +nsBufferedInputStream::Serialize(InputStreamParams& aParams, 1.493 + FileDescriptorArray& aFileDescriptors) 1.494 +{ 1.495 + BufferedInputStreamParams params; 1.496 + 1.497 + if (mStream) { 1.498 + nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream); 1.499 + MOZ_ASSERT(stream); 1.500 + 1.501 + InputStreamParams wrappedParams; 1.502 + SerializeInputStream(stream, wrappedParams, aFileDescriptors); 1.503 + 1.504 + params.optionalStream() = wrappedParams; 1.505 + } 1.506 + else { 1.507 + params.optionalStream() = mozilla::void_t(); 1.508 + } 1.509 + 1.510 + params.bufferSize() = mBufferSize; 1.511 + 1.512 + aParams = params; 1.513 +} 1.514 + 1.515 +bool 1.516 +nsBufferedInputStream::Deserialize(const InputStreamParams& aParams, 1.517 + const FileDescriptorArray& aFileDescriptors) 1.518 +{ 1.519 + if (aParams.type() != InputStreamParams::TBufferedInputStreamParams) { 1.520 + NS_ERROR("Received unknown parameters from the other process!"); 1.521 + return false; 1.522 + } 1.523 + 1.524 + const BufferedInputStreamParams& params = 1.525 + aParams.get_BufferedInputStreamParams(); 1.526 + const OptionalInputStreamParams& wrappedParams = params.optionalStream(); 1.527 + 1.528 + nsCOMPtr<nsIInputStream> stream; 1.529 + if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) { 1.530 + stream = DeserializeInputStream(wrappedParams.get_InputStreamParams(), 1.531 + aFileDescriptors); 1.532 + if (!stream) { 1.533 + NS_WARNING("Failed to deserialize wrapped stream!"); 1.534 + return false; 1.535 + } 1.536 + } 1.537 + else { 1.538 + NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t, 1.539 + "Unknown type for OptionalInputStreamParams!"); 1.540 + } 1.541 + 1.542 + nsresult rv = Init(stream, params.bufferSize()); 1.543 + NS_ENSURE_SUCCESS(rv, false); 1.544 + 1.545 + return true; 1.546 +} 1.547 + 1.548 +//////////////////////////////////////////////////////////////////////////////// 1.549 +// nsBufferedOutputStream 1.550 + 1.551 +NS_IMPL_ADDREF_INHERITED(nsBufferedOutputStream, nsBufferedStream) 1.552 +NS_IMPL_RELEASE_INHERITED(nsBufferedOutputStream, nsBufferedStream) 1.553 +// This QI uses NS_INTERFACE_MAP_ENTRY_CONDITIONAL to check for 1.554 +// non-nullness of mSafeStream. 1.555 +NS_INTERFACE_MAP_BEGIN(nsBufferedOutputStream) 1.556 + NS_INTERFACE_MAP_ENTRY(nsIOutputStream) 1.557 + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISafeOutputStream, mSafeStream) 1.558 + NS_INTERFACE_MAP_ENTRY(nsIBufferedOutputStream) 1.559 + NS_INTERFACE_MAP_ENTRY(nsIStreamBufferAccess) 1.560 +NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream) 1.561 + 1.562 +nsresult 1.563 +nsBufferedOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) 1.564 +{ 1.565 + NS_ENSURE_NO_AGGREGATION(aOuter); 1.566 + 1.567 + nsBufferedOutputStream* stream = new nsBufferedOutputStream(); 1.568 + if (stream == nullptr) 1.569 + return NS_ERROR_OUT_OF_MEMORY; 1.570 + NS_ADDREF(stream); 1.571 + nsresult rv = stream->QueryInterface(aIID, aResult); 1.572 + NS_RELEASE(stream); 1.573 + return rv; 1.574 +} 1.575 + 1.576 +NS_IMETHODIMP 1.577 +nsBufferedOutputStream::Init(nsIOutputStream* stream, uint32_t bufferSize) 1.578 +{ 1.579 + // QI stream to an nsISafeOutputStream, to see if we should support it 1.580 + mSafeStream = do_QueryInterface(stream); 1.581 + 1.582 + return nsBufferedStream::Init(stream, bufferSize); 1.583 +} 1.584 + 1.585 +NS_IMETHODIMP 1.586 +nsBufferedOutputStream::Close() 1.587 +{ 1.588 + nsresult rv1, rv2 = NS_OK, rv3; 1.589 + rv1 = Flush(); 1.590 + // If we fail to Flush all the data, then we close anyway and drop the 1.591 + // remaining data in the buffer. We do this because it's what Unix does 1.592 + // for fclose and close. However, we report the error from Flush anyway. 1.593 + if (mStream) { 1.594 + rv2 = Sink()->Close(); 1.595 + NS_RELEASE(mStream); 1.596 + } 1.597 + rv3 = nsBufferedStream::Close(); 1.598 + if (NS_FAILED(rv1)) return rv1; 1.599 + if (NS_FAILED(rv2)) return rv2; 1.600 + return rv3; 1.601 +} 1.602 + 1.603 +NS_IMETHODIMP 1.604 +nsBufferedOutputStream::Write(const char *buf, uint32_t count, uint32_t *result) 1.605 +{ 1.606 + nsresult rv = NS_OK; 1.607 + uint32_t written = 0; 1.608 + while (count > 0) { 1.609 + uint32_t amt = std::min(count, mBufferSize - mCursor); 1.610 + if (amt > 0) { 1.611 + memcpy(mBuffer + mCursor, buf + written, amt); 1.612 + written += amt; 1.613 + count -= amt; 1.614 + mCursor += amt; 1.615 + if (mFillPoint < mCursor) 1.616 + mFillPoint = mCursor; 1.617 + } 1.618 + else { 1.619 + NS_ASSERTION(mFillPoint, "iloop in nsBufferedOutputStream::Write!"); 1.620 + rv = Flush(); 1.621 + if (NS_FAILED(rv)) break; 1.622 + } 1.623 + } 1.624 + *result = written; 1.625 + return (written > 0) ? NS_OK : rv; 1.626 +} 1.627 + 1.628 +NS_IMETHODIMP 1.629 +nsBufferedOutputStream::Flush() 1.630 +{ 1.631 + nsresult rv; 1.632 + uint32_t amt; 1.633 + if (!mStream) { 1.634 + // Stream already cancelled/flushed; probably because of previous error. 1.635 + return NS_OK; 1.636 + } 1.637 + rv = Sink()->Write(mBuffer, mFillPoint, &amt); 1.638 + if (NS_FAILED(rv)) return rv; 1.639 + mBufferStartOffset += amt; 1.640 + if (amt == mFillPoint) { 1.641 + mFillPoint = mCursor = 0; 1.642 + return NS_OK; // flushed everything 1.643 + } 1.644 + 1.645 + // slide the remainder down to the start of the buffer 1.646 + // |<-------------->|<---|----->| 1.647 + // b a c s 1.648 + uint32_t rem = mFillPoint - amt; 1.649 + memcpy(mBuffer, mBuffer + amt, rem); 1.650 + mFillPoint = mCursor = rem; 1.651 + return NS_ERROR_FAILURE; // didn't flush all 1.652 +} 1.653 + 1.654 +// nsISafeOutputStream 1.655 +NS_IMETHODIMP 1.656 +nsBufferedOutputStream::Finish() 1.657 +{ 1.658 + // flush the stream, to write out any buffered data... 1.659 + nsresult rv = nsBufferedOutputStream::Flush(); 1.660 + if (NS_FAILED(rv)) 1.661 + NS_WARNING("failed to flush buffered data! possible dataloss"); 1.662 + 1.663 + // ... and finish the underlying stream... 1.664 + if (NS_SUCCEEDED(rv)) 1.665 + rv = mSafeStream->Finish(); 1.666 + else 1.667 + Sink()->Close(); 1.668 + 1.669 + // ... and close the buffered stream, so any further attempts to flush/close 1.670 + // the buffered stream won't cause errors. 1.671 + nsBufferedStream::Close(); 1.672 + 1.673 + return rv; 1.674 +} 1.675 + 1.676 +static NS_METHOD 1.677 +nsReadFromInputStream(nsIOutputStream* outStr, 1.678 + void* closure, 1.679 + char* toRawSegment, 1.680 + uint32_t offset, 1.681 + uint32_t count, 1.682 + uint32_t *readCount) 1.683 +{ 1.684 + nsIInputStream* fromStream = (nsIInputStream*)closure; 1.685 + return fromStream->Read(toRawSegment, count, readCount); 1.686 +} 1.687 + 1.688 +NS_IMETHODIMP 1.689 +nsBufferedOutputStream::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval) 1.690 +{ 1.691 + return WriteSegments(nsReadFromInputStream, inStr, count, _retval); 1.692 +} 1.693 + 1.694 +NS_IMETHODIMP 1.695 +nsBufferedOutputStream::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval) 1.696 +{ 1.697 + *_retval = 0; 1.698 + nsresult rv; 1.699 + while (count > 0) { 1.700 + uint32_t left = std::min(count, mBufferSize - mCursor); 1.701 + if (left == 0) { 1.702 + rv = Flush(); 1.703 + if (NS_FAILED(rv)) 1.704 + return rv; 1.705 + 1.706 + continue; 1.707 + } 1.708 + 1.709 + uint32_t read = 0; 1.710 + rv = reader(this, closure, mBuffer + mCursor, *_retval, left, &read); 1.711 + 1.712 + if (NS_FAILED(rv)) // If we have written some data, return ok 1.713 + return (*_retval > 0) ? NS_OK : rv; 1.714 + mCursor += read; 1.715 + *_retval += read; 1.716 + count -= read; 1.717 + mFillPoint = std::max(mFillPoint, mCursor); 1.718 + } 1.719 + return NS_OK; 1.720 +} 1.721 + 1.722 +NS_IMETHODIMP 1.723 +nsBufferedOutputStream::IsNonBlocking(bool *aNonBlocking) 1.724 +{ 1.725 + if (mStream) 1.726 + return Sink()->IsNonBlocking(aNonBlocking); 1.727 + return NS_ERROR_NOT_INITIALIZED; 1.728 +} 1.729 + 1.730 +NS_IMETHODIMP_(char*) 1.731 +nsBufferedOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask) 1.732 +{ 1.733 + NS_ASSERTION(mGetBufferCount == 0, "nested GetBuffer!"); 1.734 + if (mGetBufferCount != 0) 1.735 + return nullptr; 1.736 + 1.737 + if (mBufferDisabled) 1.738 + return nullptr; 1.739 + 1.740 + char* buf = mBuffer + mCursor; 1.741 + uint32_t rem = mBufferSize - mCursor; 1.742 + if (rem == 0) { 1.743 + if (NS_FAILED(Flush())) 1.744 + return nullptr; 1.745 + buf = mBuffer + mCursor; 1.746 + rem = mBufferSize - mCursor; 1.747 + } 1.748 + 1.749 + uint32_t mod = (NS_PTR_TO_INT32(buf) & aAlignMask); 1.750 + if (mod) { 1.751 + uint32_t pad = aAlignMask + 1 - mod; 1.752 + if (pad > rem) 1.753 + return nullptr; 1.754 + 1.755 + memset(buf, 0, pad); 1.756 + mCursor += pad; 1.757 + buf += pad; 1.758 + rem -= pad; 1.759 + } 1.760 + 1.761 + if (aLength > rem) 1.762 + return nullptr; 1.763 + mGetBufferCount++; 1.764 + return buf; 1.765 +} 1.766 + 1.767 +NS_IMETHODIMP_(void) 1.768 +nsBufferedOutputStream::PutBuffer(char* aBuffer, uint32_t aLength) 1.769 +{ 1.770 + NS_ASSERTION(mGetBufferCount == 1, "stray PutBuffer!"); 1.771 + if (--mGetBufferCount != 0) 1.772 + return; 1.773 + 1.774 + NS_ASSERTION(mCursor + aLength <= mBufferSize, "PutBuffer botch"); 1.775 + mCursor += aLength; 1.776 + if (mFillPoint < mCursor) 1.777 + mFillPoint = mCursor; 1.778 +} 1.779 + 1.780 +NS_IMETHODIMP 1.781 +nsBufferedOutputStream::DisableBuffering() 1.782 +{ 1.783 + NS_ASSERTION(!mBufferDisabled, "redundant call to DisableBuffering!"); 1.784 + NS_ASSERTION(mGetBufferCount == 0, 1.785 + "DisableBuffer call between GetBuffer and PutBuffer!"); 1.786 + if (mGetBufferCount != 0) 1.787 + return NS_ERROR_UNEXPECTED; 1.788 + 1.789 + // Empty the buffer so nsBufferedStream::Tell works. 1.790 + nsresult rv = Flush(); 1.791 + if (NS_FAILED(rv)) 1.792 + return rv; 1.793 + 1.794 + mBufferDisabled = true; 1.795 + return NS_OK; 1.796 +} 1.797 + 1.798 +NS_IMETHODIMP 1.799 +nsBufferedOutputStream::EnableBuffering() 1.800 +{ 1.801 + NS_ASSERTION(mBufferDisabled, "gratuitous call to EnableBuffering!"); 1.802 + mBufferDisabled = false; 1.803 + return NS_OK; 1.804 +} 1.805 + 1.806 +NS_IMETHODIMP 1.807 +nsBufferedOutputStream::GetUnbufferedStream(nsISupports* *aStream) 1.808 +{ 1.809 + // Empty the buffer so subsequent i/o trumps any buffered data. 1.810 + if (mFillPoint) { 1.811 + nsresult rv = Flush(); 1.812 + if (NS_FAILED(rv)) 1.813 + return rv; 1.814 + } 1.815 + 1.816 + *aStream = mStream; 1.817 + NS_IF_ADDREF(*aStream); 1.818 + return NS_OK; 1.819 +} 1.820 + 1.821 +////////////////////////////////////////////////////////////////////////////////