/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// If the underlying stream isn't a random access store, then fail early. // We could possibly succeed for the case where the seek position denotes // something that happens to be read into the buffer, but that would make // the failure data-dependent.
nsresult rv;
nsCOMPtr<nsISeekableStream> ras = do_QueryInterface(mStream, &rv); if (NS_FAILED(rv)) {
NS_WARNING("mStream doesn't QI to nsISeekableStream"); return rv;
}
// Let mCursor point into the existing buffer if the new position is // between the current cursor and the mFillPoint "fencepost" -- the // client may never get around to a Read or Write after this Seek. // Read and Write worry about flushing and filling in that event. // But if we're at EOF, make sure to pass the seek through to the // underlying stream, because it may have auto-closed itself and // needs to reopen.
uint32_t offsetInBuffer = uint32_t(absPos - mBufferStartOffset); if (offsetInBuffer <= mFillPoint && !mEOF) {
METER(bufstats.mSeeksWithinBuffer++);
mCursor = offsetInBuffer; return NS_OK;
}
METER(bufstats.mSeeksOutsideBuffer++);
METER(bufstats.mBufferReadUponSeek += mCursor);
METER(bufstats.mBufferUnreadUponSeek += mFillPoint - mCursor);
rv = Flush(); if (NS_FAILED(rv)) { #ifdef DEBUG
NS_WARNING( "(debug) Flush returned error within nsBufferedStream::Seek, so we " "exit early."); #endif return rv;
}
rv = ras->Seek(whence, offset); if (NS_FAILED(rv)) { #ifdef DEBUG
NS_WARNING( "(debug) Error: ras->Seek() returned error within " "nsBufferedStream::Seek, so we exit early."); #endif return rv;
}
mEOF = false;
// Recompute whether the offset we're seeking to is in our buffer. // Note that we need to recompute because Flush() might have // changed mBufferStartOffset.
offsetInBuffer = uint32_t(absPos - mBufferStartOffset); if (offsetInBuffer <= mFillPoint) { // It's safe to just set mCursor to offsetInBuffer. In particular, we // want to avoid calling Fill() here since we already have the data that // was seeked to and calling Fill() might auto-close our underlying // stream in some cases.
mCursor = offsetInBuffer; return NS_OK;
}
METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS)
bufstats.mBigSeek[bufstats.mBigSeekIndex]
.mOldOffset = mBufferStartOffset + int64_t(mCursor)); const int64_t minus1 = -1; if (absPos == minus1) { // then we had the SEEK_END case, above
int64_t tellPos;
rv = ras->Tell(&tellPos);
mBufferStartOffset = tellPos; if (NS_FAILED(rv)) { return rv;
}
} else {
mBufferStartOffset = absPos;
}
METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS)
bufstats.mBigSeek[bufstats.mBigSeekIndex++]
.mNewOffset = mBufferStartOffset);
mFillPoint = mCursor = 0;
// If we seeked back to the start, then don't fill the buffer // right now in case this is a lazily-opened file stream. // We'll fill on the first read, like we did initially. if (whence == nsISeekableStream::NS_SEEK_SET && offset == 0) { return NS_OK;
} return Fill();
}
NS_INTERFACE_MAP_BEGIN(nsBufferedInputStream) // Unfortunately there isn't a macro that combines ambiguous and conditional, // and as far as I can tell, no other class would need such a macro. if (mIsAsyncInputStream && aIID.Equals(NS_GET_IID(nsIInputStream))) {
foundInterface = static_cast<nsIInputStream*>(static_cast<nsIAsyncInputStream*>(this));
} elseif (!mIsAsyncInputStream && aIID.Equals(NS_GET_IID(nsIInputStream))) {
foundInterface = static_cast<nsIInputStream*>( static_cast<nsIBufferedInputStream*>(this));
} else
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIBufferedInputStream)
NS_INTERFACE_MAP_ENTRY(nsIBufferedInputStream)
NS_INTERFACE_MAP_ENTRY(nsIStreamBufferAccess)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
mIsIPCSerializable)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream, mIsAsyncInputStream)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
mIsAsyncInputStream)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
mIsCloneableInputStream)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLength, mIsInputStreamLength)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStreamLength,
mIsAsyncInputStreamLength)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLengthCallback,
mIsAsyncInputStreamLength)
NS_IMPL_QUERY_CLASSINFO(nsBufferedInputStream)
NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream)
nsresult rv;
int32_t rem = int32_t(mFillPoint - mCursor); if (rem > 0) { // slide the remainder down to the start of the buffer // |<------------->|<--rem-->|<--->| // b c f s
memcpy(mBuffer, mBuffer + mCursor, rem);
}
mBufferStartOffset += mCursor;
mFillPoint = rem;
mCursor = 0;
bool nsBufferedInputStream::Deserialize(const InputStreamParams& aParams) { if (aParams.type() != InputStreamParams::TBufferedInputStreamParams) {
NS_ERROR("Received unknown parameters from the other process!"); returnfalse;
}
// If we don't have the buffer, the inputStream has been already closed. // If mBufferStartOffset is not 0, the stream has been seeked or read. // In both case the cloning is not supported. if (!mBuffer || mBufferStartOffset) { return NS_OK;
}
NS_IMETHODIMP
nsBufferedInputStream::OnInputStreamLengthReady(
nsIAsyncInputStreamLength* aStream, int64_t aLength) {
nsCOMPtr<nsIInputStreamLengthCallback> callback;
{
MutexAutoLock lock(mMutex); // We have been canceled in the meanwhile. if (!mAsyncInputStreamLengthCallback) { return NS_OK;
}
NS_IMETHODIMP
nsBufferedOutputStream::Init(nsIOutputStream* stream, uint32_t bufferSize) { // QI stream to an nsISafeOutputStream, to see if we should support it
mSafeStream = do_QueryInterface(stream);
NS_IMETHODIMP
nsBufferedOutputStream::Close() { if (!mStream) { return NS_OK;
}
nsresult rv1, rv2 = NS_OK;
rv1 = Flush();
#ifdef DEBUG if (NS_FAILED(rv1)) {
NS_WARNING( "(debug) Flush() inside nsBufferedOutputStream::Close() returned error " "(rv1).");
} #endif
// If we fail to Flush all the data, then we close anyway and drop the // remaining data in the buffer. We do this because it's what Unix does // for fclose and close. However, we report the error from Flush anyway. if (mStream) {
rv2 = Sink()->Close(); #ifdef DEBUG if (NS_FAILED(rv2)) {
NS_WARNING( "(debug) Sink->Close() inside nsBufferedOutputStream::Close() " "returned error (rv2).");
} #endif
}
nsBufferedStream::Close();
if (NS_FAILED(rv1)) { return rv1;
} if (NS_FAILED(rv2)) { return rv2;
} return NS_OK;
}
NS_IMETHODIMP
nsBufferedOutputStream::Write(constchar* buf, uint32_t count,
uint32_t* result) {
nsresult rv = NS_OK;
uint32_t written = 0;
*result = 0; if (!mStream) { // We special case this situation. // We should catch the failure, NS_BASE_STREAM_CLOSED ASAP, here. // If we don't, eventually Flush() is called in the while loop below // after so many writes. // However, Flush() returns NS_OK when mStream is null (!!), // and we don't get a meaningful error, NS_BASE_STREAM_CLOSED, // soon enough when we use buffered output. #ifdef DEBUG
NS_WARNING( "(info) nsBufferedOutputStream::Write returns NS_BASE_STREAM_CLOSED " "immediately (mStream==null)."); #endif return NS_BASE_STREAM_CLOSED;
}
RecursiveMutexAutoLock lock(mBufferMutex); while (count > 0) {
uint32_t amt = std::min(count, mBufferSize - mCursor); if (amt > 0) {
memcpy(mBuffer + mCursor, buf + written, amt);
written += amt;
count -= amt;
mCursor += amt; if (mFillPoint < mCursor) mFillPoint = mCursor;
} else {
NS_ASSERTION(mFillPoint, "loop in nsBufferedOutputStream::Write!");
rv = Flush(); if (NS_FAILED(rv)) { #ifdef DEBUG
NS_WARNING( "(debug) Flush() returned error in nsBufferedOutputStream::Write."); #endif break;
}
}
}
*result = written; return (written > 0) ? NS_OK : rv;
}
NS_IMETHODIMP
nsBufferedOutputStream::Flush() {
nsresult rv;
uint32_t amt; if (!mStream) { // Stream already cancelled/flushed; probably because of previous error. return NS_OK;
} // optimize : some code within C-C needs to call Seek -> Flush() often. if (mFillPoint == 0) { return NS_OK;
}
RecursiveMutexAutoLock lock(mBufferMutex);
rv = Sink()->Write(mBuffer, mFillPoint, &amt); if (NS_FAILED(rv)) { return rv;
}
mBufferStartOffset += amt; if (amt == mFillPoint) {
mFillPoint = mCursor = 0; return NS_OK; // flushed everything
}
// slide the remainder down to the start of the buffer // |<-------------->|<---|----->| // b a c s
uint32_t rem = mFillPoint - amt;
memmove(mBuffer, mBuffer + amt, rem);
mFillPoint = mCursor = rem; return NS_ERROR_FAILURE; // didn't flush all
}
// nsISafeOutputStream
NS_IMETHODIMP
nsBufferedOutputStream::Finish() { // flush the stream, to write out any buffered data...
nsresult rv1 = nsBufferedOutputStream::Flush();
nsresult rv2 = NS_OK;
if (NS_FAILED(rv1)) {
NS_WARNING( "(debug) nsBufferedOutputStream::Flush() failed in " "nsBufferedOutputStream::Finish()! Possible dataloss.");
rv2 = Sink()->Close(); if (NS_FAILED(rv2)) {
NS_WARNING( "(debug) Sink()->Close() failed in nsBufferedOutputStream::Finish()! " "Possible dataloss.");
}
} else {
rv2 = mSafeStream->Finish(); if (NS_FAILED(rv2)) {
NS_WARNING( "(debug) mSafeStream->Finish() failed within " "nsBufferedOutputStream::Flush()! Possible dataloss.");
}
}
// ... and close the buffered stream, so any further attempts to flush/close // the buffered stream won't cause errors.
nsBufferedStream::Close();
// We want to return the errors precisely from Finish() // and mimick the existing error handling in // nsBufferedOutputStream::Close() as reference.
if (NS_FAILED(rv1)) { return rv1;
} if (NS_FAILED(rv2)) { return rv2;
} return NS_OK;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.