2014-12-12 22:12:27 +03:00
|
|
|
/* -*- 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/. */
|
|
|
|
|
|
|
|
#include "mozilla/SnappyCompressOutputStream.h"
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include "nsStreamUtils.h"
|
|
|
|
#include "snappy/snappy.h"
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS(SnappyCompressOutputStream, nsIOutputStream);
|
|
|
|
|
|
|
|
// static
|
|
|
|
const size_t
|
|
|
|
SnappyCompressOutputStream::kMaxBlockSize = snappy::kBlockSize;
|
|
|
|
|
|
|
|
SnappyCompressOutputStream::SnappyCompressOutputStream(nsIOutputStream* aBaseStream,
|
|
|
|
size_t aBlockSize)
|
|
|
|
: mBaseStream(aBaseStream)
|
|
|
|
, mBlockSize(std::min(aBlockSize, kMaxBlockSize))
|
|
|
|
, mNextByte(0)
|
|
|
|
, mCompressedBufferLength(0)
|
|
|
|
, mStreamIdentifierWritten(false)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mBlockSize > 0);
|
|
|
|
|
|
|
|
// This implementation only supports sync base streams. Verify this in debug
|
|
|
|
// builds. Note, this can be simpler than the check in
|
|
|
|
// SnappyUncompressInputStream because we don't have to deal with the
|
|
|
|
// nsStringInputStream oddness of being non-blocking and sync.
|
|
|
|
#ifdef DEBUG
|
|
|
|
bool baseNonBlocking;
|
|
|
|
nsresult rv = mBaseStream->IsNonBlocking(&baseNonBlocking);
|
|
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
|
|
MOZ_ASSERT(!baseNonBlocking);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
SnappyCompressOutputStream::BlockSize() const
|
|
|
|
{
|
|
|
|
return mBlockSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
SnappyCompressOutputStream::Close()
|
|
|
|
{
|
|
|
|
if (!mBaseStream) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult rv = Flush();
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
|
|
|
|
|
|
|
mBaseStream->Close();
|
|
|
|
mBaseStream = nullptr;
|
|
|
|
|
|
|
|
mBuffer = nullptr;
|
|
|
|
mCompressedBuffer = nullptr;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
SnappyCompressOutputStream::Flush()
|
|
|
|
{
|
|
|
|
if (!mBaseStream) {
|
|
|
|
return NS_BASE_STREAM_CLOSED;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult rv = FlushToBaseStream();
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
|
|
|
|
|
|
|
mBaseStream->Flush();
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
SnappyCompressOutputStream::Write(const char* aBuf, uint32_t aCount,
|
|
|
|
uint32_t* aResultOut)
|
|
|
|
{
|
|
|
|
return WriteSegments(NS_CopySegmentToBuffer, const_cast<char*>(aBuf), aCount,
|
|
|
|
aResultOut);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
SnappyCompressOutputStream::WriteFrom(nsIInputStream*, uint32_t, uint32_t*)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
SnappyCompressOutputStream::WriteSegments(nsReadSegmentFun aReader,
|
|
|
|
void* aClosure,
|
|
|
|
uint32_t aCount,
|
|
|
|
uint32_t* aBytesWrittenOut)
|
|
|
|
{
|
|
|
|
*aBytesWrittenOut = 0;
|
|
|
|
|
|
|
|
if (!mBaseStream) {
|
|
|
|
return NS_BASE_STREAM_CLOSED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mBuffer) {
|
2015-01-28 12:00:40 +03:00
|
|
|
mBuffer.reset(new (fallible) char[mBlockSize]);
|
2014-12-12 22:12:27 +03:00
|
|
|
if (NS_WARN_IF(!mBuffer)) {
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (aCount > 0) {
|
|
|
|
// Determine how much space is left in our flat, uncompressed buffer.
|
|
|
|
MOZ_ASSERT(mNextByte <= mBlockSize);
|
|
|
|
uint32_t remaining = mBlockSize - mNextByte;
|
|
|
|
|
|
|
|
// If it is full, then compress and flush the data to the base stream.
|
|
|
|
if (remaining == 0) {
|
|
|
|
nsresult rv = FlushToBaseStream();
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
|
|
|
|
|
|
|
// Now the entire buffer should be available for copying.
|
|
|
|
MOZ_ASSERT(!mNextByte);
|
|
|
|
remaining = mBlockSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t numToRead = std::min(remaining, aCount);
|
|
|
|
uint32_t numRead = 0;
|
|
|
|
|
|
|
|
nsresult rv = aReader(this, aClosure, &mBuffer[mNextByte],
|
|
|
|
*aBytesWrittenOut, numToRead, &numRead);
|
|
|
|
|
|
|
|
// As defined in nsIOutputStream.idl, do not pass reader func errors.
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// End-of-file
|
|
|
|
if (numRead == 0) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
mNextByte += numRead;
|
|
|
|
*aBytesWrittenOut += numRead;
|
|
|
|
aCount -= numRead;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
SnappyCompressOutputStream::IsNonBlocking(bool* aNonBlockingOut)
|
|
|
|
{
|
|
|
|
*aNonBlockingOut = false;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
SnappyCompressOutputStream::~SnappyCompressOutputStream()
|
|
|
|
{
|
|
|
|
Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SnappyCompressOutputStream::FlushToBaseStream()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mBaseStream);
|
|
|
|
|
|
|
|
// Lazily create the compressed buffer on our first flush. This
|
|
|
|
// allows us to report OOM during stream operation. This buffer
|
|
|
|
// will then get re-used until the stream is closed.
|
|
|
|
if (!mCompressedBuffer) {
|
|
|
|
mCompressedBufferLength = MaxCompressedBufferLength(mBlockSize);
|
2015-01-28 12:00:40 +03:00
|
|
|
mCompressedBuffer.reset(new (fallible) char[mCompressedBufferLength]);
|
2014-12-12 22:12:27 +03:00
|
|
|
if (NS_WARN_IF(!mCompressedBuffer)) {
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The first chunk must be a StreamIdentifier chunk. Write it out
|
|
|
|
// if we have not done so already.
|
|
|
|
nsresult rv = MaybeFlushStreamIdentifier();
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
|
|
|
|
|
|
|
// Compress the data to our internal compressed buffer.
|
|
|
|
size_t compressedLength;
|
|
|
|
rv = WriteCompressedData(mCompressedBuffer.get(), mCompressedBufferLength,
|
|
|
|
mBuffer.get(), mNextByte, &compressedLength);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
|
|
|
MOZ_ASSERT(compressedLength > 0);
|
|
|
|
|
|
|
|
mNextByte = 0;
|
|
|
|
|
|
|
|
// Write the compressed buffer out to the base stream.
|
|
|
|
uint32_t numWritten = 0;
|
|
|
|
rv = WriteAll(mCompressedBuffer.get(), compressedLength, &numWritten);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
|
|
|
MOZ_ASSERT(compressedLength == numWritten);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SnappyCompressOutputStream::MaybeFlushStreamIdentifier()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mCompressedBuffer);
|
|
|
|
|
|
|
|
if (mStreamIdentifierWritten) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build the StreamIdentifier in our compressed buffer.
|
|
|
|
size_t compressedLength;
|
|
|
|
nsresult rv = WriteStreamIdentifier(mCompressedBuffer.get(),
|
|
|
|
mCompressedBufferLength,
|
|
|
|
&compressedLength);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
|
|
|
|
|
|
|
// Write the compressed buffer out to the base stream.
|
|
|
|
uint32_t numWritten = 0;
|
|
|
|
rv = WriteAll(mCompressedBuffer.get(), compressedLength, &numWritten);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
|
|
|
MOZ_ASSERT(compressedLength == numWritten);
|
|
|
|
|
|
|
|
mStreamIdentifierWritten = true;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SnappyCompressOutputStream::WriteAll(const char* aBuf, uint32_t aCount,
|
|
|
|
uint32_t* aBytesWrittenOut)
|
|
|
|
{
|
|
|
|
*aBytesWrittenOut = 0;
|
|
|
|
|
|
|
|
if (!mBaseStream) {
|
|
|
|
return NS_BASE_STREAM_CLOSED;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t offset = 0;
|
|
|
|
while (aCount > 0) {
|
|
|
|
uint32_t numWritten = 0;
|
|
|
|
nsresult rv = mBaseStream->Write(aBuf + offset, aCount, &numWritten);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
|
|
|
offset += numWritten;
|
|
|
|
aCount -= numWritten;
|
|
|
|
*aBytesWrittenOut += numWritten;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace mozilla
|