/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */
/* * The storage stream provides an internal buffer that can be filled by a * client using a single output stream. One or more independent input streams * can be created to read the data out non-destructively. The implementation * uses a segmented buffer internally to avoid realloc'ing of large buffers, * with the attendant performance loss and heap fragmentation.
*/
using mozilla::MutexAutoLock; using mozilla::ipc::InputStreamParams; using mozilla::ipc::StringInputStreamParams;
// // Log module for StorageStream logging... // // To enable logging (see prlog.h for full details): // // set MOZ_LOG=StorageStreamLog:5 // set MOZ_LOG_FILE=storage.log // // This enables LogLevel::Debug level information and places all output in // the file storage.log. // static mozilla::LazyLogModule sStorageStreamLog("nsStorageStream"); #ifdef LOG # undef LOG #endif #define LOG(args) MOZ_LOG(sStorageStreamLog, mozilla::LogLevel::Debug, args)
MutexAutoLock lock(mMutex); if (NS_WARN_IF(!mSegmentedBuffer)) { return NS_ERROR_NOT_INITIALIZED;
}
if (mWriteInProgress) { return NS_ERROR_NOT_AVAILABLE;
}
if (mActiveSegmentBorrows > 0) { return NS_ERROR_NOT_AVAILABLE;
}
nsresult rv = Seek(aStartingOffset); if (NS_FAILED(rv)) { return rv;
}
// Enlarge the last segment in the buffer so that it is the same size as // all the other segments in the buffer. (It may have been realloc'ed // smaller in the Close() method.) if (mLastSegmentNum >= 0) if (mSegmentedBuffer->ReallocLastSegment(mSegmentSize)) { // Need to re-Seek, since realloc changed segment base pointer
rv = Seek(aStartingOffset); if (NS_FAILED(rv)) { return rv;
}
}
// Shrink the final segment in the segmented buffer to the minimum size // needed to contain the data, so as to conserve memory. if (segmentOffset && !mActiveSegmentBorrows) {
mSegmentedBuffer->ReallocLastSegment(segmentOffset);
}
mWriteCursor = 0;
mSegmentEnd = 0;
LOG(("nsStorageStream [%p] Close mWriteCursor=%p mSegmentEnd=%p\n", this,
mWriteCursor, mSegmentEnd));
// If no segments have been created yet, create one even if we don't have // to write any data; this enables creating an input stream which reads from // the very end of the data for any amount of data in the stream (i.e. // this stream contains N bytes of data and newInputStream(N) is called), // even for N=0 (with the caveat that we require .write("", 0) be called to // initialize internal buffers). bool firstTime = mSegmentedBuffer->GetSegmentCount() == 0; while (remaining || MOZ_UNLIKELY(firstTime)) {
firstTime = false;
uint32_t availableInSegment = mSegmentEnd - mWriteCursor; if (!availableInSegment) {
mWriteCursor = mSegmentedBuffer->AppendNewSegment(); if (!mWriteCursor) {
mSegmentEnd = 0; return NS_ERROR_OUT_OF_MEMORY;
}
mLastSegmentNum++;
mSegmentEnd = mWriteCursor + mSegmentSize;
availableInSegment = mSegmentEnd - mWriteCursor;
LOG(
("nsStorageStream [%p] Write (new seg) mWriteCursor=%p " "mSegmentEnd=%p\n", this, mWriteCursor, mSegmentEnd));
}
// Truncate the buffer by deleting the end segments
nsresult nsStorageStream::SetLengthLocked(uint32_t aLength) { if (NS_WARN_IF(!mSegmentedBuffer)) { return NS_ERROR_NOT_INITIALIZED;
}
if (mWriteInProgress) { return NS_ERROR_NOT_AVAILABLE;
}
if (mActiveSegmentBorrows) { return NS_ERROR_NOT_AVAILABLE;
}
if (aLength > mLogicalLength) { return NS_ERROR_INVALID_ARG;
}
nsresult nsStorageStream::Seek(int32_t aPosition) { if (NS_WARN_IF(!mSegmentedBuffer)) { return NS_ERROR_NOT_INITIALIZED;
}
// An argument of -1 means "seek to end of stream" if (aPosition == -1) {
aPosition = mLogicalLength;
}
// Seeking beyond the buffer end is illegal if ((uint32_t)aPosition > mLogicalLength) { return NS_ERROR_INVALID_ARG;
}
// Seeking backwards in the write stream results in truncation
SetLengthLocked(aPosition);
// Special handling for seek to start-of-buffer if (aPosition == 0) {
mWriteCursor = 0;
mSegmentEnd = 0;
LOG(("nsStorageStream [%p] Seek mWriteCursor=%p mSegmentEnd=%p\n", this,
mWriteCursor, mSegmentEnd)); return NS_OK;
}
// Segment may have changed, so reset pointers
mWriteCursor = mSegmentedBuffer->GetSegment(mLastSegmentNum);
NS_ASSERTION(mWriteCursor, "null mWriteCursor");
mSegmentEnd = mWriteCursor + mSegmentSize;
// Adjust write cursor for current segment offset. This test is necessary // because SegNum may reference the next-to-be-allocated segment, in which // case we need to be pointing at the end of the last segment.
int32_t segmentOffset = SegOffset(aPosition); if (segmentOffset == 0 && (SegNum(aPosition) > (uint32_t)mLastSegmentNum)) {
mWriteCursor = mSegmentEnd;
} else {
mWriteCursor += segmentOffset;
}
// There can be many nsStorageInputStreams for a single nsStorageStream class nsStorageInputStream final : public nsIInputStream, public nsISeekableStream, public nsIIPCSerializableInputStream, public nsICloneableInputStream { public:
nsStorageInputStream(nsStorageStream* aStorageStream, uint32_t aSegmentSize)
: mStorageStream(aStorageStream),
mReadCursor(0),
mSegmentEnd(0),
mSegmentNum(0),
mSegmentSize(aSegmentSize),
mLogicalCursor(0),
mStatus(NS_OK) {}
private:
RefPtr<nsStorageStream> mStorageStream;
uint32_t mReadCursor; // Next memory location to read byte, or 0
uint32_t mSegmentEnd; // One byte past end of current buffer segment
uint32_t mSegmentNum; // Segment number containing read cursor
uint32_t mSegmentSize; // All segments, except the last, are of this size
uint32_t mLogicalCursor; // Logical offset into stream
nsresult mStatus;
remainingCapacity = aCount; while (remainingCapacity) { constchar* cur = nullptr;
{
MutexAutoLock lock(mStorageStream->mMutex);
availableInSegment = mSegmentEnd - mReadCursor; if (!availableInSegment) {
uint32_t available = mStorageStream->mLogicalLength - mLogicalCursor; if (!available) { break;
}
// We have data in the stream, but if mSegmentEnd is zero, then we // were likely constructed prior to any data being written into // the stream. Therefore, if mSegmentEnd is non-zero, we should // move into the next segment; otherwise, we should stay in this // segment so our input state can be updated and we can properly // perform the initial read. if (mSegmentEnd > 0) {
mSegmentNum++;
}
mReadCursor = 0;
mSegmentEnd = XPCOM_MIN(mSegmentSize, available);
availableInSegment = mSegmentEnd;
}
cur = mStorageStream->mSegmentedBuffer->GetSegment(mSegmentNum);
mStorageStream->mActiveSegmentBorrows++;
} auto dropBorrow = mozilla::MakeScopeExit([&] {
MutexAutoLock lock(mStorageStream->mMutex);
mStorageStream->mActiveSegmentBorrows--;
});
if (*aNumRead == 0 && isWriteInProgress) { return NS_BASE_STREAM_WOULD_BLOCK;
}
return NS_OK;
}
NS_IMETHODIMP
nsStorageInputStream::IsNonBlocking(bool* aNonBlocking) { // TODO: This class should implement nsIAsyncInputStream so that callers // have some way of dealing with NS_BASE_STREAM_WOULD_BLOCK errors.
bool nsStorageInputStream::Deserialize(const InputStreamParams& aParams) {
MOZ_ASSERT_UNREACHABLE( "We should never attempt to deserialize a storage " "input stream."); returnfalse;
}
// Undefine LOG, so that other .cpp files (or their includes) won't complain // about it already being defined, when we build in unified mode. #undef LOG
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet)
¤
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.