Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/io/SnappyFrameUtils.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/SnappyFrameUtils.h"
8
9
#include "crc32c.h"
10
#include "mozilla/EndianUtils.h"
11
#include "nsDebug.h"
12
#include "snappy/snappy.h"
13
14
namespace {
15
16
using mozilla::detail::SnappyFrameUtils;
17
using mozilla::NativeEndian;
18
19
SnappyFrameUtils::ChunkType ReadChunkType(uint8_t aByte)
20
0
{
21
0
  if (aByte == 0xff)  {
22
0
    return SnappyFrameUtils::StreamIdentifier;
23
0
  } else if (aByte == 0x00) {
24
0
    return SnappyFrameUtils::CompressedData;
25
0
  } else if (aByte == 0x01) {
26
0
    return SnappyFrameUtils::UncompressedData;
27
0
  } else if (aByte == 0xfe) {
28
0
    return SnappyFrameUtils::Padding;
29
0
  }
30
0
31
0
  return SnappyFrameUtils::Reserved;
32
0
}
33
34
void WriteChunkType(char* aDest, SnappyFrameUtils::ChunkType aType)
35
0
{
36
0
  unsigned char* dest = reinterpret_cast<unsigned char*>(aDest);
37
0
  if (aType == SnappyFrameUtils::StreamIdentifier) {
38
0
    *dest = 0xff;
39
0
  } else if (aType == SnappyFrameUtils::CompressedData) {
40
0
    *dest = 0x00;
41
0
  } else if (aType == SnappyFrameUtils::UncompressedData) {
42
0
    *dest = 0x01;
43
0
  } else if (aType == SnappyFrameUtils::Padding) {
44
0
    *dest = 0xfe;
45
0
  } else {
46
0
    *dest = 0x02;
47
0
  }
48
0
}
49
50
void WriteUInt24(char* aBuf, uint32_t aVal)
51
0
{
52
0
  MOZ_ASSERT(!(aVal & 0xff000000));
53
0
  uint32_t tmp = NativeEndian::swapToLittleEndian(aVal);
54
0
  memcpy(aBuf, &tmp, 3);
55
0
}
56
57
uint32_t ReadUInt24(const char* aBuf)
58
0
{
59
0
  uint32_t val = 0;
60
0
  memcpy(&val, aBuf, 3);
61
0
  return NativeEndian::swapFromLittleEndian(val);
62
0
}
63
64
// This mask is explicitly defined in the snappy framing_format.txt file.
65
uint32_t MaskChecksum(uint32_t aValue)
66
0
{
67
0
  return ((aValue >> 15) | (aValue << 17)) + 0xa282ead8;
68
0
}
69
70
} // namespace
71
72
namespace mozilla {
73
namespace detail {
74
75
using mozilla::LittleEndian;
76
77
// static
78
nsresult
79
SnappyFrameUtils::WriteStreamIdentifier(char* aDest, size_t aDestLength,
80
                                        size_t* aBytesWrittenOut)
81
0
{
82
0
  if (NS_WARN_IF(aDestLength < (kHeaderLength + kStreamIdentifierDataLength))) {
83
0
    return NS_ERROR_NOT_AVAILABLE;
84
0
  }
85
0
86
0
  WriteChunkType(aDest, StreamIdentifier);
87
0
  aDest[1] = 0x06;  // Data length
88
0
  aDest[2] = 0x00;
89
0
  aDest[3] = 0x00;
90
0
  aDest[4] = 0x73;  // "sNaPpY"
91
0
  aDest[5] = 0x4e;
92
0
  aDest[6] = 0x61;
93
0
  aDest[7] = 0x50;
94
0
  aDest[8] = 0x70;
95
0
  aDest[9] = 0x59;
96
0
97
0
  static_assert(kHeaderLength + kStreamIdentifierDataLength == 10,
98
0
                "StreamIdentifier chunk should be exactly 10 bytes long");
99
0
  *aBytesWrittenOut = kHeaderLength + kStreamIdentifierDataLength;
100
0
101
0
  return NS_OK;
102
0
}
103
104
// static
105
nsresult
106
SnappyFrameUtils::WriteCompressedData(char* aDest, size_t aDestLength,
107
                                      const char* aData, size_t aDataLength,
108
                                      size_t* aBytesWrittenOut)
109
0
{
110
0
  *aBytesWrittenOut = 0;
111
0
112
0
  size_t neededLength = MaxCompressedBufferLength(aDataLength);
113
0
  if (NS_WARN_IF(aDestLength < neededLength)) {
114
0
    return NS_ERROR_NOT_AVAILABLE;
115
0
  }
116
0
117
0
  size_t offset = 0;
118
0
119
0
  WriteChunkType(aDest, CompressedData);
120
0
  offset += kChunkTypeLength;
121
0
122
0
  // Skip length for now and write it out after we know the compressed length.
123
0
  size_t lengthOffset = offset;
124
0
  offset += kChunkLengthLength;
125
0
126
0
  uint32_t crc = ComputeCrc32c(~0, reinterpret_cast<const unsigned char*>(aData),
127
0
                               aDataLength);
128
0
  uint32_t maskedCrc = MaskChecksum(crc);
129
0
  LittleEndian::writeUint32(aDest + offset, maskedCrc);
130
0
  offset += kCRCLength;
131
0
132
0
  size_t compressedLength;
133
0
  snappy::RawCompress(aData, aDataLength, aDest + offset, &compressedLength);
134
0
135
0
  // Go back and write the data length.
136
0
  size_t dataLength = compressedLength + kCRCLength;
137
0
  WriteUInt24(aDest + lengthOffset, dataLength);
138
0
139
0
  *aBytesWrittenOut = kHeaderLength + dataLength;
140
0
141
0
  return NS_OK;
142
0
}
143
144
// static
145
nsresult
146
SnappyFrameUtils::ParseHeader(const char* aSource, size_t aSourceLength,
147
                              ChunkType* aTypeOut, size_t* aDataLengthOut)
148
0
{
149
0
  if (NS_WARN_IF(aSourceLength < kHeaderLength)) {
150
0
    return NS_ERROR_NOT_AVAILABLE;
151
0
  }
152
0
153
0
  *aTypeOut = ReadChunkType(aSource[0]);
154
0
  *aDataLengthOut = ReadUInt24(aSource + kChunkTypeLength);
155
0
156
0
  return NS_OK;
157
0
}
158
159
// static
160
nsresult
161
SnappyFrameUtils::ParseData(char* aDest, size_t aDestLength,
162
                            ChunkType aType, const char* aData,
163
                            size_t aDataLength,
164
                            size_t* aBytesWrittenOut, size_t* aBytesReadOut)
165
0
{
166
0
  switch(aType) {
167
0
    case StreamIdentifier:
168
0
      return ParseStreamIdentifier(aDest, aDestLength, aData, aDataLength,
169
0
                                   aBytesWrittenOut, aBytesReadOut);
170
0
171
0
    case CompressedData:
172
0
      return ParseCompressedData(aDest, aDestLength, aData, aDataLength,
173
0
                                 aBytesWrittenOut, aBytesReadOut);
174
0
175
0
    // TODO: support other snappy chunk types
176
0
    default:
177
0
      MOZ_ASSERT_UNREACHABLE("Unsupported snappy framing chunk type.");
178
0
      return NS_ERROR_NOT_IMPLEMENTED;
179
0
  }
180
0
}
181
182
// static
183
nsresult
184
SnappyFrameUtils::ParseStreamIdentifier(char*, size_t,
185
                                        const char* aData, size_t aDataLength,
186
                                        size_t* aBytesWrittenOut,
187
                                        size_t* aBytesReadOut)
188
0
{
189
0
  *aBytesWrittenOut = 0;
190
0
  *aBytesReadOut = 0;
191
0
  if (NS_WARN_IF(aDataLength != kStreamIdentifierDataLength ||
192
0
                 aData[0] != 0x73 ||
193
0
                 aData[1] != 0x4e ||
194
0
                 aData[2] != 0x61 ||
195
0
                 aData[3] != 0x50 ||
196
0
                 aData[4] != 0x70 ||
197
0
                 aData[5] != 0x59)) {
198
0
    return NS_ERROR_CORRUPTED_CONTENT;
199
0
  }
200
0
  *aBytesReadOut = aDataLength;
201
0
  return NS_OK;
202
0
}
203
204
// static
205
nsresult
206
SnappyFrameUtils::ParseCompressedData(char* aDest, size_t aDestLength,
207
                                      const char* aData, size_t aDataLength,
208
                                      size_t* aBytesWrittenOut,
209
                                      size_t* aBytesReadOut)
210
0
{
211
0
  *aBytesWrittenOut = 0;
212
0
  *aBytesReadOut = 0;
213
0
  size_t offset = 0;
214
0
215
0
  uint32_t readCrc = LittleEndian::readUint32(aData + offset);
216
0
  offset += kCRCLength;
217
0
218
0
  size_t uncompressedLength;
219
0
  if (NS_WARN_IF(!snappy::GetUncompressedLength(aData + offset,
220
0
                                                aDataLength - offset,
221
0
                                                &uncompressedLength))) {
222
0
    return NS_ERROR_CORRUPTED_CONTENT;
223
0
  }
224
0
225
0
  if (NS_WARN_IF(aDestLength < uncompressedLength)) {
226
0
    return NS_ERROR_NOT_AVAILABLE;
227
0
  }
228
0
229
0
  if (NS_WARN_IF(!snappy::RawUncompress(aData + offset, aDataLength - offset,
230
0
                                        aDest))) {
231
0
    return NS_ERROR_CORRUPTED_CONTENT;
232
0
  }
233
0
234
0
  uint32_t crc = ComputeCrc32c(~0, reinterpret_cast<const unsigned char*>(aDest),
235
0
                               uncompressedLength);
236
0
  uint32_t maskedCrc = MaskChecksum(crc);
237
0
  if (NS_WARN_IF(readCrc != maskedCrc)) {
238
0
    return NS_ERROR_CORRUPTED_CONTENT;
239
0
  }
240
0
241
0
  *aBytesWrittenOut = uncompressedLength;
242
0
  *aBytesReadOut = aDataLength;
243
0
244
0
  return NS_OK;
245
0
}
246
247
// static
248
size_t
249
SnappyFrameUtils::MaxCompressedBufferLength(size_t aSourceLength)
250
0
{
251
0
  size_t neededLength = kHeaderLength;
252
0
  neededLength += kCRCLength;
253
0
  neededLength += snappy::MaxCompressedLength(aSourceLength);
254
0
  return neededLength;
255
0
}
256
257
} // namespace detail
258
} // namespace mozilla