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"
10 #include "nsStreamUtils.h"
11 #include "snappy/snappy.h"
15 NS_IMPL_ISUPPORTS(SnappyCompressOutputStream
, nsIOutputStream
);
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
)),
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.
35 nsresult rv
= mBaseStream
->IsNonBlocking(&baseNonBlocking
);
36 MOZ_ASSERT(NS_SUCCEEDED(rv
));
37 MOZ_ASSERT(!baseNonBlocking
);
41 size_t SnappyCompressOutputStream::BlockSize() const { return mBlockSize
; }
44 SnappyCompressOutputStream::Close() {
49 nsresult rv
= Flush();
50 if (NS_WARN_IF(NS_FAILED(rv
))) {
55 mBaseStream
= nullptr;
58 mCompressedBuffer
= nullptr;
64 SnappyCompressOutputStream::Flush() {
66 return NS_BASE_STREAM_CLOSED
;
69 nsresult rv
= FlushToBaseStream();
70 if (NS_WARN_IF(NS_FAILED(rv
))) {
80 SnappyCompressOutputStream::StreamStatus() {
82 return NS_BASE_STREAM_CLOSED
;
84 return mBaseStream
->StreamStatus();
88 SnappyCompressOutputStream::Write(const char* aBuf
, uint32_t aCount
,
89 uint32_t* aResultOut
) {
90 return WriteSegments(NS_CopyBufferToSegment
, const_cast<char*>(aBuf
), aCount
,
95 SnappyCompressOutputStream::WriteFrom(nsIInputStream
*, uint32_t, uint32_t*) {
96 return NS_ERROR_NOT_IMPLEMENTED
;
100 SnappyCompressOutputStream::WriteSegments(nsReadSegmentFun aReader
,
101 void* aClosure
, uint32_t aCount
,
102 uint32_t* aBytesWrittenOut
) {
103 *aBytesWrittenOut
= 0;
106 return NS_BASE_STREAM_CLOSED
;
110 mBuffer
.reset(new (fallible
) char[mBlockSize
]);
111 if (NS_WARN_IF(!mBuffer
)) {
112 return NS_ERROR_OUT_OF_MEMORY
;
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
))) {
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.
149 mNextByte
+= numRead
;
150 *aBytesWrittenOut
+= numRead
;
158 SnappyCompressOutputStream::IsNonBlocking(bool* aNonBlockingOut
) {
159 *aNonBlockingOut
= false;
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
))) {
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
))) {
193 MOZ_ASSERT(compressedLength
> 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
))) {
203 MOZ_ASSERT(compressedLength
== numWritten
);
208 nsresult
SnappyCompressOutputStream::MaybeFlushStreamIdentifier() {
209 MOZ_ASSERT(mCompressedBuffer
);
211 if (mStreamIdentifierWritten
) {
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
))) {
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
))) {
229 MOZ_ASSERT(compressedLength
== numWritten
);
231 mStreamIdentifierWritten
= true;
236 nsresult
SnappyCompressOutputStream::WriteAll(const char* aBuf
, uint32_t aCount
,
237 uint32_t* aBytesWrittenOut
) {
238 *aBytesWrittenOut
= 0;
241 return NS_BASE_STREAM_CLOSED
;
246 uint32_t numWritten
= 0;
247 nsresult rv
= mBaseStream
->Write(aBuf
+ offset
, aCount
, &numWritten
);
248 if (NS_WARN_IF(NS_FAILED(rv
))) {
251 offset
+= numWritten
;
252 aCount
-= numWritten
;
253 *aBytesWrittenOut
+= numWritten
;
259 } // namespace mozilla