Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/io/SnappyCompressOutputStream.cpp
Line
Count
Source (jump to first uncovered line)
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/. */
6
7
#include "mozilla/SnappyCompressOutputStream.h"
8
9
#include <algorithm>
10
#include "nsStreamUtils.h"
11
#include "snappy/snappy.h"
12
13
namespace mozilla {
14
15
NS_IMPL_ISUPPORTS(SnappyCompressOutputStream, nsIOutputStream);
16
17
// static
18
const size_t
19
SnappyCompressOutputStream::kMaxBlockSize = snappy::kBlockSize;
20
21
SnappyCompressOutputStream::SnappyCompressOutputStream(nsIOutputStream* aBaseStream,
22
                                                       size_t aBlockSize)
23
 : mBaseStream(aBaseStream)
24
 , mBlockSize(std::min(aBlockSize, kMaxBlockSize))
25
 , mNextByte(0)
26
 , mCompressedBufferLength(0)
27
 , mStreamIdentifierWritten(false)
28
0
{
29
0
  MOZ_ASSERT(mBlockSize > 0);
30
0
31
0
  // This implementation only supports sync base streams.  Verify this in debug
32
0
  // builds.  Note, this can be simpler than the check in
33
0
  // SnappyUncompressInputStream because we don't have to deal with the
34
0
  // nsStringInputStream oddness of being non-blocking and sync.
35
#ifdef DEBUG
36
  bool baseNonBlocking;
37
  nsresult rv = mBaseStream->IsNonBlocking(&baseNonBlocking);
38
  MOZ_ASSERT(NS_SUCCEEDED(rv));
39
  MOZ_ASSERT(!baseNonBlocking);
40
#endif
41
}
42
43
size_t
44
SnappyCompressOutputStream::BlockSize() const
45
0
{
46
0
  return mBlockSize;
47
0
}
48
49
NS_IMETHODIMP
50
SnappyCompressOutputStream::Close()
51
0
{
52
0
  if (!mBaseStream) {
53
0
    return NS_OK;
54
0
  }
55
0
56
0
  nsresult rv = Flush();
57
0
  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
58
0
59
0
  mBaseStream->Close();
60
0
  mBaseStream = nullptr;
61
0
62
0
  mBuffer = nullptr;
63
0
  mCompressedBuffer = nullptr;
64
0
65
0
  return NS_OK;
66
0
}
67
68
NS_IMETHODIMP
69
SnappyCompressOutputStream::Flush()
70
0
{
71
0
  if (!mBaseStream) {
72
0
    return NS_BASE_STREAM_CLOSED;
73
0
  }
74
0
75
0
  nsresult rv = FlushToBaseStream();
76
0
  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
77
0
78
0
  mBaseStream->Flush();
79
0
80
0
  return NS_OK;
81
0
}
82
83
NS_IMETHODIMP
84
SnappyCompressOutputStream::Write(const char* aBuf, uint32_t aCount,
85
                                  uint32_t* aResultOut)
86
0
{
87
0
  return WriteSegments(NS_CopySegmentToBuffer, const_cast<char*>(aBuf), aCount,
88
0
                       aResultOut);
89
0
}
90
91
NS_IMETHODIMP
92
SnappyCompressOutputStream::WriteFrom(nsIInputStream*, uint32_t, uint32_t*)
93
0
{
94
0
  return NS_ERROR_NOT_IMPLEMENTED;
95
0
}
96
97
NS_IMETHODIMP
98
SnappyCompressOutputStream::WriteSegments(nsReadSegmentFun aReader,
99
                                          void* aClosure,
100
                                          uint32_t aCount,
101
                                          uint32_t* aBytesWrittenOut)
102
0
{
103
0
  *aBytesWrittenOut = 0;
104
0
105
0
  if (!mBaseStream) {
106
0
    return NS_BASE_STREAM_CLOSED;
107
0
  }
108
0
109
0
  if (!mBuffer) {
110
0
    mBuffer.reset(new (fallible) char[mBlockSize]);
111
0
    if (NS_WARN_IF(!mBuffer)) {
112
0
      return NS_ERROR_OUT_OF_MEMORY;
113
0
    }
114
0
  }
115
0
116
0
  while (aCount > 0) {
117
0
    // Determine how much space is left in our flat, uncompressed buffer.
118
0
    MOZ_ASSERT(mNextByte <= mBlockSize);
119
0
    uint32_t remaining = mBlockSize - mNextByte;
120
0
121
0
    // If it is full, then compress and flush the data to the base stream.
122
0
    if (remaining == 0) {
123
0
      nsresult rv = FlushToBaseStream();
124
0
      if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
125
0
126
0
      // Now the entire buffer should be available for copying.
127
0
      MOZ_ASSERT(!mNextByte);
128
0
      remaining = mBlockSize;
129
0
    }
130
0
131
0
    uint32_t numToRead = std::min(remaining, aCount);
132
0
    uint32_t numRead = 0;
133
0
134
0
    nsresult rv = aReader(this, aClosure, &mBuffer[mNextByte],
135
0
                          *aBytesWrittenOut, numToRead, &numRead);
136
0
137
0
    // As defined in nsIOutputStream.idl, do not pass reader func errors.
138
0
    if (NS_FAILED(rv)) {
139
0
      return NS_OK;
140
0
    }
141
0
142
0
    // End-of-file
143
0
    if (numRead == 0) {
144
0
      return NS_OK;
145
0
    }
146
0
147
0
    mNextByte += numRead;
148
0
    *aBytesWrittenOut += numRead;
149
0
    aCount -= numRead;
150
0
  }
151
0
152
0
  return NS_OK;
153
0
}
154
155
NS_IMETHODIMP
156
SnappyCompressOutputStream::IsNonBlocking(bool* aNonBlockingOut)
157
0
{
158
0
  *aNonBlockingOut = false;
159
0
  return NS_OK;
160
0
}
161
162
SnappyCompressOutputStream::~SnappyCompressOutputStream()
163
0
{
164
0
  Close();
165
0
}
166
167
nsresult
168
SnappyCompressOutputStream::FlushToBaseStream()
169
0
{
170
0
  MOZ_ASSERT(mBaseStream);
171
0
172
0
  // Lazily create the compressed buffer on our first flush.  This
173
0
  // allows us to report OOM during stream operation.  This buffer
174
0
  // will then get re-used until the stream is closed.
175
0
  if (!mCompressedBuffer) {
176
0
    mCompressedBufferLength = MaxCompressedBufferLength(mBlockSize);
177
0
    mCompressedBuffer.reset(new (fallible) char[mCompressedBufferLength]);
178
0
    if (NS_WARN_IF(!mCompressedBuffer)) {
179
0
      return NS_ERROR_OUT_OF_MEMORY;
180
0
    }
181
0
  }
182
0
183
0
  // The first chunk must be a StreamIdentifier chunk.  Write it out
184
0
  // if we have not done so already.
185
0
  nsresult rv = MaybeFlushStreamIdentifier();
186
0
  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
187
0
188
0
  // Compress the data to our internal compressed buffer.
189
0
  size_t compressedLength;
190
0
  rv = WriteCompressedData(mCompressedBuffer.get(), mCompressedBufferLength,
191
0
                           mBuffer.get(), mNextByte, &compressedLength);
192
0
  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
193
0
  MOZ_ASSERT(compressedLength > 0);
194
0
195
0
  mNextByte = 0;
196
0
197
0
  // Write the compressed buffer out to the base stream.
198
0
  uint32_t numWritten = 0;
199
0
  rv = WriteAll(mCompressedBuffer.get(), compressedLength, &numWritten);
200
0
  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
201
0
  MOZ_ASSERT(compressedLength == numWritten);
202
0
203
0
  return NS_OK;
204
0
}
205
206
nsresult
207
SnappyCompressOutputStream::MaybeFlushStreamIdentifier()
208
0
{
209
0
  MOZ_ASSERT(mCompressedBuffer);
210
0
211
0
  if (mStreamIdentifierWritten) {
212
0
    return NS_OK;
213
0
  }
214
0
215
0
  // Build the StreamIdentifier in our compressed buffer.
216
0
  size_t compressedLength;
217
0
  nsresult rv = WriteStreamIdentifier(mCompressedBuffer.get(),
218
0
                                      mCompressedBufferLength,
219
0
                                      &compressedLength);
220
0
  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
221
0
222
0
  // Write the compressed buffer out to the base stream.
223
0
  uint32_t numWritten = 0;
224
0
  rv = WriteAll(mCompressedBuffer.get(), compressedLength, &numWritten);
225
0
  if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
226
0
  MOZ_ASSERT(compressedLength == numWritten);
227
0
228
0
  mStreamIdentifierWritten = true;
229
0
230
0
  return NS_OK;
231
0
}
232
233
nsresult
234
SnappyCompressOutputStream::WriteAll(const char* aBuf, uint32_t aCount,
235
                                     uint32_t* aBytesWrittenOut)
236
0
{
237
0
  *aBytesWrittenOut = 0;
238
0
239
0
  if (!mBaseStream) {
240
0
    return NS_BASE_STREAM_CLOSED;
241
0
  }
242
0
243
0
  uint32_t offset = 0;
244
0
  while (aCount > 0) {
245
0
    uint32_t numWritten = 0;
246
0
    nsresult rv = mBaseStream->Write(aBuf + offset, aCount, &numWritten);
247
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
248
0
    offset += numWritten;
249
0
    aCount -= numWritten;
250
0
    *aBytesWrittenOut += numWritten;
251
0
  }
252
0
253
0
  return NS_OK;
254
0
}
255
256
} // namespace mozilla