Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / xpcom / io / SnappyCompressOutputStream.cpp
blobebe9e1073cd5d99584095d45e8cffc5ba8009e3d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/SnappyCompressOutputStream.h"
9 #include <algorithm>
10 #include "nsStreamUtils.h"
11 #include "snappy/snappy.h"
13 namespace mozilla {
15 NS_IMPL_ISUPPORTS(SnappyCompressOutputStream, nsIOutputStream);
17 // static
18 const size_t SnappyCompressOutputStream::kMaxBlockSize = snappy::kBlockSize;
20 SnappyCompressOutputStream::SnappyCompressOutputStream(
21 nsIOutputStream* aBaseStream, size_t aBlockSize)
22 : mBaseStream(aBaseStream),
23 mBlockSize(std::min(aBlockSize, kMaxBlockSize)),
24 mNextByte(0),
25 mCompressedBufferLength(0),
26 mStreamIdentifierWritten(false) {
27 MOZ_ASSERT(mBlockSize > 0);
29 // This implementation only supports sync base streams. Verify this in debug
30 // builds. Note, this can be simpler than the check in
31 // SnappyUncompressInputStream because we don't have to deal with the
32 // nsStringInputStream oddness of being non-blocking and sync.
33 #ifdef DEBUG
34 bool baseNonBlocking;
35 nsresult rv = mBaseStream->IsNonBlocking(&baseNonBlocking);
36 MOZ_ASSERT(NS_SUCCEEDED(rv));
37 MOZ_ASSERT(!baseNonBlocking);
38 #endif
41 size_t SnappyCompressOutputStream::BlockSize() const { return mBlockSize; }
43 NS_IMETHODIMP
44 SnappyCompressOutputStream::Close() {
45 if (!mBaseStream) {
46 return NS_OK;
49 nsresult rv = Flush();
50 if (NS_WARN_IF(NS_FAILED(rv))) {
51 return rv;
54 mBaseStream->Close();
55 mBaseStream = nullptr;
57 mBuffer = nullptr;
58 mCompressedBuffer = nullptr;
60 return NS_OK;
63 NS_IMETHODIMP
64 SnappyCompressOutputStream::Flush() {
65 if (!mBaseStream) {
66 return NS_BASE_STREAM_CLOSED;
69 nsresult rv = FlushToBaseStream();
70 if (NS_WARN_IF(NS_FAILED(rv))) {
71 return rv;
74 mBaseStream->Flush();
76 return NS_OK;
79 NS_IMETHODIMP
80 SnappyCompressOutputStream::StreamStatus() {
81 if (!mBaseStream) {
82 return NS_BASE_STREAM_CLOSED;
84 return mBaseStream->StreamStatus();
87 NS_IMETHODIMP
88 SnappyCompressOutputStream::Write(const char* aBuf, uint32_t aCount,
89 uint32_t* aResultOut) {
90 return WriteSegments(NS_CopyBufferToSegment, const_cast<char*>(aBuf), aCount,
91 aResultOut);
94 NS_IMETHODIMP
95 SnappyCompressOutputStream::WriteFrom(nsIInputStream*, uint32_t, uint32_t*) {
96 return NS_ERROR_NOT_IMPLEMENTED;
99 NS_IMETHODIMP
100 SnappyCompressOutputStream::WriteSegments(nsReadSegmentFun aReader,
101 void* aClosure, uint32_t aCount,
102 uint32_t* aBytesWrittenOut) {
103 *aBytesWrittenOut = 0;
105 if (!mBaseStream) {
106 return NS_BASE_STREAM_CLOSED;
109 if (!mBuffer) {
110 mBuffer.reset(new (fallible) char[mBlockSize]);
111 if (NS_WARN_IF(!mBuffer)) {
112 return NS_ERROR_OUT_OF_MEMORY;
116 while (aCount > 0) {
117 // Determine how much space is left in our flat, uncompressed buffer.
118 MOZ_ASSERT(mNextByte <= mBlockSize);
119 uint32_t remaining = mBlockSize - mNextByte;
121 // If it is full, then compress and flush the data to the base stream.
122 if (remaining == 0) {
123 nsresult rv = FlushToBaseStream();
124 if (NS_WARN_IF(NS_FAILED(rv))) {
125 return rv;
128 // Now the entire buffer should be available for copying.
129 MOZ_ASSERT(!mNextByte);
130 remaining = mBlockSize;
133 uint32_t numToRead = std::min(remaining, aCount);
134 uint32_t numRead = 0;
136 nsresult rv = aReader(this, aClosure, &mBuffer[mNextByte],
137 *aBytesWrittenOut, numToRead, &numRead);
139 // As defined in nsIOutputStream.idl, do not pass reader func errors.
140 if (NS_FAILED(rv)) {
141 return NS_OK;
144 // End-of-file
145 if (numRead == 0) {
146 return NS_OK;
149 mNextByte += numRead;
150 *aBytesWrittenOut += numRead;
151 aCount -= numRead;
154 return NS_OK;
157 NS_IMETHODIMP
158 SnappyCompressOutputStream::IsNonBlocking(bool* aNonBlockingOut) {
159 *aNonBlockingOut = false;
160 return NS_OK;
163 SnappyCompressOutputStream::~SnappyCompressOutputStream() { Close(); }
165 nsresult SnappyCompressOutputStream::FlushToBaseStream() {
166 MOZ_ASSERT(mBaseStream);
168 // Lazily create the compressed buffer on our first flush. This
169 // allows us to report OOM during stream operation. This buffer
170 // will then get re-used until the stream is closed.
171 if (!mCompressedBuffer) {
172 mCompressedBufferLength = MaxCompressedBufferLength(mBlockSize);
173 mCompressedBuffer.reset(new (fallible) char[mCompressedBufferLength]);
174 if (NS_WARN_IF(!mCompressedBuffer)) {
175 return NS_ERROR_OUT_OF_MEMORY;
179 // The first chunk must be a StreamIdentifier chunk. Write it out
180 // if we have not done so already.
181 nsresult rv = MaybeFlushStreamIdentifier();
182 if (NS_WARN_IF(NS_FAILED(rv))) {
183 return rv;
186 // Compress the data to our internal compressed buffer.
187 size_t compressedLength;
188 rv = WriteCompressedData(mCompressedBuffer.get(), mCompressedBufferLength,
189 mBuffer.get(), mNextByte, &compressedLength);
190 if (NS_WARN_IF(NS_FAILED(rv))) {
191 return rv;
193 MOZ_ASSERT(compressedLength > 0);
195 mNextByte = 0;
197 // Write the compressed buffer out to the base stream.
198 uint32_t numWritten = 0;
199 rv = WriteAll(mCompressedBuffer.get(), compressedLength, &numWritten);
200 if (NS_WARN_IF(NS_FAILED(rv))) {
201 return rv;
203 MOZ_ASSERT(compressedLength == numWritten);
205 return NS_OK;
208 nsresult SnappyCompressOutputStream::MaybeFlushStreamIdentifier() {
209 MOZ_ASSERT(mCompressedBuffer);
211 if (mStreamIdentifierWritten) {
212 return NS_OK;
215 // Build the StreamIdentifier in our compressed buffer.
216 size_t compressedLength;
217 nsresult rv = WriteStreamIdentifier(
218 mCompressedBuffer.get(), mCompressedBufferLength, &compressedLength);
219 if (NS_WARN_IF(NS_FAILED(rv))) {
220 return rv;
223 // Write the compressed buffer out to the base stream.
224 uint32_t numWritten = 0;
225 rv = WriteAll(mCompressedBuffer.get(), compressedLength, &numWritten);
226 if (NS_WARN_IF(NS_FAILED(rv))) {
227 return rv;
229 MOZ_ASSERT(compressedLength == numWritten);
231 mStreamIdentifierWritten = true;
233 return NS_OK;
236 nsresult SnappyCompressOutputStream::WriteAll(const char* aBuf, uint32_t aCount,
237 uint32_t* aBytesWrittenOut) {
238 *aBytesWrittenOut = 0;
240 if (!mBaseStream) {
241 return NS_BASE_STREAM_CLOSED;
244 uint32_t offset = 0;
245 while (aCount > 0) {
246 uint32_t numWritten = 0;
247 nsresult rv = mBaseStream->Write(aBuf + offset, aCount, &numWritten);
248 if (NS_WARN_IF(NS_FAILED(rv))) {
249 return rv;
251 offset += numWritten;
252 aCount -= numWritten;
253 *aBytesWrittenOut += numWritten;
256 return NS_OK;
259 } // namespace mozilla