nsStorageStream.cpp revision 677833bc953b6cb418c701facbdcf4aa18d6c44e
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* 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.
*/
#include "nsStorageStream.h"
#include "nsSegmentedBuffer.h"
#include "nsCOMPtr.h"
#include "prbit.h"
#include "nsIInputStream.h"
#include "nsISeekableStream.h"
#include "prlog.h"
#include "nsInt64.h"
#if defined(PR_LOGGING)
//
// Log module for StorageStream logging...
//
// To enable logging (see prlog.h for full details):
//
// set NSPR_LOG_MODULES=StorageStreamLog:5
// set NSPR_LOG_FILE=nspr.log
//
// this enables PR_LOG_DEBUG level information and places all output in
// the file nspr.log
//
#endif /* PR_LOGGING */
{
#if defined(PR_LOGGING)
//
// Initialize the global PRLogModule for socket transport logging
// if necessary...
//
if (nsnull == StorageStreamLog) {
}
#endif /* PR_LOGGING */
("Creating nsStorageStream [%x].\n", this));
}
{
if (mSegmentedBuffer)
delete mSegmentedBuffer;
}
{
mSegmentedBuffer = new nsSegmentedBuffer();
if (!mSegmentedBuffer)
return NS_ERROR_OUT_OF_MEMORY;
// Segment size must be a power of two
return NS_ERROR_INVALID_ARG;
}
{
if (mWriteInProgress)
return NS_ERROR_NOT_AVAILABLE;
// 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)
// Need to re-Seek, since realloc might have changed segment base pointer
NS_ADDREF(this);
return NS_OK;
}
{
// Shrink the final segment in the segmented buffer to the minimum size
// needed to contain the data, so as to conserve memory.
if (segmentOffset)
mWriteCursor = 0;
mSegmentEnd = 0;
("nsStorageStream [%x] Close mWriteCursor=%x mSegmentEnd=%x\n",
this, mWriteCursor, mSegmentEnd));
return NS_OK;
}
{
return NS_OK;
}
{
const char* readCursor;
("nsStorageStream [%x] Write mWriteCursor=%x mSegmentEnd=%x aCount=%d\n",
while (remaining) {
if (!availableInSegment) {
if (!mWriteCursor) {
mSegmentEnd = 0;
goto out;
}
("nsStorageStream [%x] Write (new seg) mWriteCursor=%x mSegmentEnd=%x\n",
this, mWriteCursor, mSegmentEnd));
}
readCursor += count;
mWriteCursor += count;
("nsStorageStream [%x] Writing mWriteCursor=%x mSegmentEnd=%x count=%d\n",
};
out:
("nsStorageStream [%x] Wrote mWriteCursor=%x mSegmentEnd=%x numWritten=%d\n",
return rv;
}
{
NS_NOTREACHED("WriteFrom");
return NS_ERROR_NOT_IMPLEMENTED;
}
nsStorageStream::WriteSegments(nsReadSegmentFun reader, void * closure, PRUint32 count, PRUint32 *_retval)
{
NS_NOTREACHED("WriteSegments");
return NS_ERROR_NOT_IMPLEMENTED;
}
{
*aNonBlocking = PR_TRUE;
return NS_OK;
}
{
return NS_OK;
}
// Truncate the buffer by deleting the end segments
{
if (mWriteInProgress)
return NS_ERROR_NOT_AVAILABLE;
if (aLength > mLogicalLength)
return NS_ERROR_INVALID_ARG;
if (segmentOffset == 0)
while (newLastSegmentNum < mLastSegmentNum) {
}
return NS_OK;
}
{
return NS_OK;
}
{
// An argument of -1 means "seek to end of stream"
if (aPosition == -1)
// Seeking beyond the buffer end is illegal
return NS_ERROR_INVALID_ARG;
// Seeking backwards in the write stream results in truncation
// Special handling for seek to start-of-buffer
if (aPosition == 0) {
mWriteCursor = 0;
mSegmentEnd = 0;
("nsStorageStream [%x] Seek mWriteCursor=%x mSegmentEnd=%x\n",
this, mWriteCursor, mSegmentEnd));
return NS_OK;
}
// Segment may have changed, so reset pointers
("nsStorageStream [%x] Seek mWriteCursor=%x mSegmentEnd=%x\n",
this, mWriteCursor, mSegmentEnd));
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// There can be many nsStorageInputStreams for a single nsStorageStream
class nsStorageInputStream : public nsIInputStream
, public nsISeekableStream
{
public:
mSegmentEnd(0), mSegmentNum(0),
{
}
private:
{
}
protected:
friend class nsStorageStream;
private:
const char* mReadCursor; // Next memory location to read byte, or NULL
const char* mSegmentEnd; // One byte past end of current buffer segment
};
{
if (!inputStream)
return NS_ERROR_OUT_OF_MEMORY;
return rv;
}
return NS_OK;
}
{
return NS_OK;
}
{
return NS_OK;
}
{
char* writeCursor;
while (remainingCapacity) {
if (!availableInSegment) {
if (!available)
goto out;
}
mReadCursor += count;
writeCursor += count;
mLogicalCursor += count;
};
out:
if (*aNumRead == 0 && isWriteInProgress)
return NS_BASE_STREAM_WOULD_BLOCK;
else
return NS_OK;
}
nsStorageInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 aCount, PRUint32 *aNumRead)
{
while (remainingCapacity) {
if (!availableInSegment) {
if (!available)
goto out;
}
break;
};
out:
if (*aNumRead == 0 && isWriteInProgress)
return NS_BASE_STREAM_WOULD_BLOCK;
else
return NS_OK;
}
{
*aNonBlocking = PR_TRUE;
return NS_OK;
}
{
switch (aWhence) {
case NS_SEEK_SET:
break;
case NS_SEEK_CUR:
pos += mLogicalCursor;
break;
case NS_SEEK_END:
break;
default:
NS_NOTREACHED("unexpected whence value");
return NS_ERROR_UNEXPECTED;
}
if (pos == logicalCursor)
return NS_OK;
}
{
return NS_OK;
}
{
NS_NOTREACHED("nsStorageInputStream::SetEOF");
return NS_ERROR_NOT_IMPLEMENTED;
}
{
return NS_ERROR_INVALID_ARG;
return NS_OK;
}
{
if (!storageStream) return NS_ERROR_OUT_OF_MEMORY;
return rv;
}
*result = storageStream;
return NS_OK;
}