Blame xpcom/io/SnappyCompressOutputStream.cpp

Packit f0b94e
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
Packit f0b94e
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
Packit f0b94e
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit f0b94e
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit f0b94e
Packit f0b94e
#include "mozilla/SnappyCompressOutputStream.h"
Packit f0b94e
Packit f0b94e
#include <algorithm>
Packit f0b94e
#include "nsStreamUtils.h"
Packit f0b94e
#include "snappy/snappy.h"
Packit f0b94e
Packit f0b94e
namespace mozilla {
Packit f0b94e
Packit f0b94e
NS_IMPL_ISUPPORTS(SnappyCompressOutputStream, nsIOutputStream);
Packit f0b94e
Packit f0b94e
// static
Packit f0b94e
const size_t SnappyCompressOutputStream::kMaxBlockSize = snappy::kBlockSize;
Packit f0b94e
Packit f0b94e
SnappyCompressOutputStream::SnappyCompressOutputStream(
Packit f0b94e
    nsIOutputStream* aBaseStream, size_t aBlockSize)
Packit f0b94e
    : mBaseStream(aBaseStream),
Packit f0b94e
      mBlockSize(std::min(aBlockSize, kMaxBlockSize)),
Packit f0b94e
      mNextByte(0),
Packit f0b94e
      mCompressedBufferLength(0),
Packit f0b94e
      mStreamIdentifierWritten(false) {
Packit f0b94e
  MOZ_ASSERT(mBlockSize > 0);
Packit f0b94e
Packit f0b94e
  // This implementation only supports sync base streams.  Verify this in debug
Packit f0b94e
  // builds.  Note, this can be simpler than the check in
Packit f0b94e
  // SnappyUncompressInputStream because we don't have to deal with the
Packit f0b94e
  // nsStringInputStream oddness of being non-blocking and sync.
Packit f0b94e
#ifdef DEBUG
Packit f0b94e
  bool baseNonBlocking;
Packit f0b94e
  nsresult rv = mBaseStream->IsNonBlocking(&baseNonBlocking);
Packit f0b94e
  MOZ_ASSERT(NS_SUCCEEDED(rv));
Packit f0b94e
  MOZ_ASSERT(!baseNonBlocking);
Packit f0b94e
#endif
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
size_t SnappyCompressOutputStream::BlockSize() const { return mBlockSize; }
Packit f0b94e
Packit f0b94e
NS_IMETHODIMP
Packit f0b94e
SnappyCompressOutputStream::Close() {
Packit f0b94e
  if (!mBaseStream) {
Packit f0b94e
    return NS_OK;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  nsresult rv = Flush();
Packit f0b94e
  if (NS_WARN_IF(NS_FAILED(rv))) {
Packit f0b94e
    return rv;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  mBaseStream->Close();
Packit f0b94e
  mBaseStream = nullptr;
Packit f0b94e
Packit f0b94e
  mBuffer = nullptr;
Packit f0b94e
  mCompressedBuffer = nullptr;
Packit f0b94e
Packit f0b94e
  return NS_OK;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
NS_IMETHODIMP
Packit f0b94e
SnappyCompressOutputStream::Flush() {
Packit f0b94e
  if (!mBaseStream) {
Packit f0b94e
    return NS_BASE_STREAM_CLOSED;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  nsresult rv = FlushToBaseStream();
Packit f0b94e
  if (NS_WARN_IF(NS_FAILED(rv))) {
Packit f0b94e
    return rv;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  mBaseStream->Flush();
Packit f0b94e
Packit f0b94e
  return NS_OK;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
NS_IMETHODIMP
Packit f0b94e
SnappyCompressOutputStream::Write(const char* aBuf, uint32_t aCount,
Packit f0b94e
                                  uint32_t* aResultOut) {
Packit f0b94e
  return WriteSegments(NS_CopySegmentToBuffer, const_cast<char*>(aBuf), aCount,
Packit f0b94e
                       aResultOut);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
NS_IMETHODIMP
Packit f0b94e
SnappyCompressOutputStream::WriteFrom(nsIInputStream*, uint32_t, uint32_t*) {
Packit f0b94e
  return NS_ERROR_NOT_IMPLEMENTED;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
NS_IMETHODIMP
Packit f0b94e
SnappyCompressOutputStream::WriteSegments(nsReadSegmentFun aReader,
Packit f0b94e
                                          void* aClosure, uint32_t aCount,
Packit f0b94e
                                          uint32_t* aBytesWrittenOut) {
Packit f0b94e
  *aBytesWrittenOut = 0;
Packit f0b94e
Packit f0b94e
  if (!mBaseStream) {
Packit f0b94e
    return NS_BASE_STREAM_CLOSED;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (!mBuffer) {
Packit f0b94e
    mBuffer.reset(new (fallible) char[mBlockSize]);
Packit f0b94e
    if (NS_WARN_IF(!mBuffer)) {
Packit f0b94e
      return NS_ERROR_OUT_OF_MEMORY;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  while (aCount > 0) {
Packit f0b94e
    // Determine how much space is left in our flat, uncompressed buffer.
Packit f0b94e
    MOZ_ASSERT(mNextByte <= mBlockSize);
Packit f0b94e
    uint32_t remaining = mBlockSize - mNextByte;
Packit f0b94e
Packit f0b94e
    // If it is full, then compress and flush the data to the base stream.
Packit f0b94e
    if (remaining == 0) {
Packit f0b94e
      nsresult rv = FlushToBaseStream();
Packit f0b94e
      if (NS_WARN_IF(NS_FAILED(rv))) {
Packit f0b94e
        return rv;
Packit f0b94e
      }
Packit f0b94e
Packit f0b94e
      // Now the entire buffer should be available for copying.
Packit f0b94e
      MOZ_ASSERT(!mNextByte);
Packit f0b94e
      remaining = mBlockSize;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    uint32_t numToRead = std::min(remaining, aCount);
Packit f0b94e
    uint32_t numRead = 0;
Packit f0b94e
Packit f0b94e
    nsresult rv = aReader(this, aClosure, &mBuffer[mNextByte],
Packit f0b94e
                          *aBytesWrittenOut, numToRead, &numRead);
Packit f0b94e
Packit f0b94e
    // As defined in nsIOutputStream.idl, do not pass reader func errors.
Packit f0b94e
    if (NS_FAILED(rv)) {
Packit f0b94e
      return NS_OK;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    // End-of-file
Packit f0b94e
    if (numRead == 0) {
Packit f0b94e
      return NS_OK;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    mNextByte += numRead;
Packit f0b94e
    *aBytesWrittenOut += numRead;
Packit f0b94e
    aCount -= numRead;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return NS_OK;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
NS_IMETHODIMP
Packit f0b94e
SnappyCompressOutputStream::IsNonBlocking(bool* aNonBlockingOut) {
Packit f0b94e
  *aNonBlockingOut = false;
Packit f0b94e
  return NS_OK;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
SnappyCompressOutputStream::~SnappyCompressOutputStream() { Close(); }
Packit f0b94e
Packit f0b94e
nsresult SnappyCompressOutputStream::FlushToBaseStream() {
Packit f0b94e
  MOZ_ASSERT(mBaseStream);
Packit f0b94e
Packit f0b94e
  // Lazily create the compressed buffer on our first flush.  This
Packit f0b94e
  // allows us to report OOM during stream operation.  This buffer
Packit f0b94e
  // will then get re-used until the stream is closed.
Packit f0b94e
  if (!mCompressedBuffer) {
Packit f0b94e
    mCompressedBufferLength = MaxCompressedBufferLength(mBlockSize);
Packit f0b94e
    mCompressedBuffer.reset(new (fallible) char[mCompressedBufferLength]);
Packit f0b94e
    if (NS_WARN_IF(!mCompressedBuffer)) {
Packit f0b94e
      return NS_ERROR_OUT_OF_MEMORY;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // The first chunk must be a StreamIdentifier chunk.  Write it out
Packit f0b94e
  // if we have not done so already.
Packit f0b94e
  nsresult rv = MaybeFlushStreamIdentifier();
Packit f0b94e
  if (NS_WARN_IF(NS_FAILED(rv))) {
Packit f0b94e
    return rv;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Compress the data to our internal compressed buffer.
Packit f0b94e
  size_t compressedLength;
Packit f0b94e
  rv = WriteCompressedData(mCompressedBuffer.get(), mCompressedBufferLength,
Packit f0b94e
                           mBuffer.get(), mNextByte, &compressedLength);
Packit f0b94e
  if (NS_WARN_IF(NS_FAILED(rv))) {
Packit f0b94e
    return rv;
Packit f0b94e
  }
Packit f0b94e
  MOZ_ASSERT(compressedLength > 0);
Packit f0b94e
Packit f0b94e
  mNextByte = 0;
Packit f0b94e
Packit f0b94e
  // Write the compressed buffer out to the base stream.
Packit f0b94e
  uint32_t numWritten = 0;
Packit f0b94e
  rv = WriteAll(mCompressedBuffer.get(), compressedLength, &numWritten);
Packit f0b94e
  if (NS_WARN_IF(NS_FAILED(rv))) {
Packit f0b94e
    return rv;
Packit f0b94e
  }
Packit f0b94e
  MOZ_ASSERT(compressedLength == numWritten);
Packit f0b94e
Packit f0b94e
  return NS_OK;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nsresult SnappyCompressOutputStream::MaybeFlushStreamIdentifier() {
Packit f0b94e
  MOZ_ASSERT(mCompressedBuffer);
Packit f0b94e
Packit f0b94e
  if (mStreamIdentifierWritten) {
Packit f0b94e
    return NS_OK;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Build the StreamIdentifier in our compressed buffer.
Packit f0b94e
  size_t compressedLength;
Packit f0b94e
  nsresult rv = WriteStreamIdentifier(
Packit f0b94e
      mCompressedBuffer.get(), mCompressedBufferLength, &compressedLength);
Packit f0b94e
  if (NS_WARN_IF(NS_FAILED(rv))) {
Packit f0b94e
    return rv;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Write the compressed buffer out to the base stream.
Packit f0b94e
  uint32_t numWritten = 0;
Packit f0b94e
  rv = WriteAll(mCompressedBuffer.get(), compressedLength, &numWritten);
Packit f0b94e
  if (NS_WARN_IF(NS_FAILED(rv))) {
Packit f0b94e
    return rv;
Packit f0b94e
  }
Packit f0b94e
  MOZ_ASSERT(compressedLength == numWritten);
Packit f0b94e
Packit f0b94e
  mStreamIdentifierWritten = true;
Packit f0b94e
Packit f0b94e
  return NS_OK;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nsresult SnappyCompressOutputStream::WriteAll(const char* aBuf, uint32_t aCount,
Packit f0b94e
                                              uint32_t* aBytesWrittenOut) {
Packit f0b94e
  *aBytesWrittenOut = 0;
Packit f0b94e
Packit f0b94e
  if (!mBaseStream) {
Packit f0b94e
    return NS_BASE_STREAM_CLOSED;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  uint32_t offset = 0;
Packit f0b94e
  while (aCount > 0) {
Packit f0b94e
    uint32_t numWritten = 0;
Packit f0b94e
    nsresult rv = mBaseStream->Write(aBuf + offset, aCount, &numWritten);
Packit f0b94e
    if (NS_WARN_IF(NS_FAILED(rv))) {
Packit f0b94e
      return rv;
Packit f0b94e
    }
Packit f0b94e
    offset += numWritten;
Packit f0b94e
    aCount -= numWritten;
Packit f0b94e
    *aBytesWrittenOut += numWritten;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return NS_OK;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
}  // namespace mozilla