Line data Source code
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 0 : SnappyFrameUtils::ChunkType ReadChunkType(uint8_t aByte)
20 : {
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 : }
30 :
31 0 : return SnappyFrameUtils::Reserved;
32 : }
33 :
34 0 : void WriteChunkType(char* aDest, SnappyFrameUtils::ChunkType aType)
35 : {
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 : } else {
46 0 : *dest = 0x02;
47 : }
48 0 : }
49 :
50 0 : void WriteUInt24(char* aBuf, uint32_t aVal)
51 : {
52 0 : MOZ_ASSERT(!(aVal & 0xff000000));
53 0 : uint32_t tmp = NativeEndian::swapToLittleEndian(aVal);
54 0 : memcpy(aBuf, &tmp, 3);
55 0 : }
56 :
57 0 : uint32_t ReadUInt24(const char* aBuf)
58 : {
59 0 : uint32_t val = 0;
60 0 : memcpy(&val, aBuf, 3);
61 0 : return NativeEndian::swapFromLittleEndian(val);
62 : }
63 :
64 : // This mask is explicitly defined in the snappy framing_format.txt file.
65 0 : uint32_t MaskChecksum(uint32_t aValue)
66 : {
67 0 : return ((aValue >> 15) | (aValue << 17)) + 0xa282ead8;
68 : }
69 :
70 : } // namespace
71 :
72 : namespace mozilla {
73 : namespace detail {
74 :
75 : using mozilla::LittleEndian;
76 :
77 : // static
78 : nsresult
79 0 : SnappyFrameUtils::WriteStreamIdentifier(char* aDest, size_t aDestLength,
80 : size_t* aBytesWrittenOut)
81 : {
82 0 : if (NS_WARN_IF(aDestLength < (kHeaderLength + kStreamIdentifierDataLength))) {
83 0 : return NS_ERROR_NOT_AVAILABLE;
84 : }
85 :
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 :
97 : static_assert(kHeaderLength + kStreamIdentifierDataLength == 10,
98 : "StreamIdentifier chunk should be exactly 10 bytes long");
99 0 : *aBytesWrittenOut = kHeaderLength + kStreamIdentifierDataLength;
100 :
101 0 : return NS_OK;
102 : }
103 :
104 : // static
105 : nsresult
106 0 : SnappyFrameUtils::WriteCompressedData(char* aDest, size_t aDestLength,
107 : const char* aData, size_t aDataLength,
108 : size_t* aBytesWrittenOut)
109 : {
110 0 : *aBytesWrittenOut = 0;
111 :
112 0 : size_t neededLength = MaxCompressedBufferLength(aDataLength);
113 0 : if (NS_WARN_IF(aDestLength < neededLength)) {
114 0 : return NS_ERROR_NOT_AVAILABLE;
115 : }
116 :
117 0 : size_t offset = 0;
118 :
119 0 : WriteChunkType(aDest, CompressedData);
120 0 : offset += kChunkTypeLength;
121 :
122 : // 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 :
126 : 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 :
132 : size_t compressedLength;
133 0 : snappy::RawCompress(aData, aDataLength, aDest + offset, &compressedLength);
134 :
135 : // Go back and write the data length.
136 0 : size_t dataLength = compressedLength + kCRCLength;
137 0 : WriteUInt24(aDest + lengthOffset, dataLength);
138 :
139 0 : *aBytesWrittenOut = kHeaderLength + dataLength;
140 :
141 0 : return NS_OK;
142 : }
143 :
144 : // static
145 : nsresult
146 0 : SnappyFrameUtils::ParseHeader(const char* aSource, size_t aSourceLength,
147 : ChunkType* aTypeOut, size_t* aDataLengthOut)
148 : {
149 0 : if (NS_WARN_IF(aSourceLength < kHeaderLength)) {
150 0 : return NS_ERROR_NOT_AVAILABLE;
151 : }
152 :
153 0 : *aTypeOut = ReadChunkType(aSource[0]);
154 0 : *aDataLengthOut = ReadUInt24(aSource + kChunkTypeLength);
155 :
156 0 : return NS_OK;
157 : }
158 :
159 : // static
160 : nsresult
161 0 : 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 : {
166 0 : switch(aType) {
167 : case StreamIdentifier:
168 : return ParseStreamIdentifier(aDest, aDestLength, aData, aDataLength,
169 0 : aBytesWrittenOut, aBytesReadOut);
170 :
171 : case CompressedData:
172 : return ParseCompressedData(aDest, aDestLength, aData, aDataLength,
173 0 : aBytesWrittenOut, aBytesReadOut);
174 :
175 : // TODO: support other snappy chunk types
176 : default:
177 0 : MOZ_ASSERT_UNREACHABLE("Unsupported snappy framing chunk type.");
178 : return NS_ERROR_NOT_IMPLEMENTED;
179 : }
180 : }
181 :
182 : // static
183 : nsresult
184 0 : SnappyFrameUtils::ParseStreamIdentifier(char*, size_t,
185 : const char* aData, size_t aDataLength,
186 : size_t* aBytesWrittenOut,
187 : size_t* aBytesReadOut)
188 : {
189 0 : *aBytesWrittenOut = 0;
190 0 : *aBytesReadOut = 0;
191 0 : if (NS_WARN_IF(aDataLength != kStreamIdentifierDataLength ||
192 : aData[0] != 0x73 ||
193 : aData[1] != 0x4e ||
194 : aData[2] != 0x61 ||
195 : aData[3] != 0x50 ||
196 : aData[4] != 0x70 ||
197 : aData[5] != 0x59)) {
198 0 : return NS_ERROR_CORRUPTED_CONTENT;
199 : }
200 0 : *aBytesReadOut = aDataLength;
201 0 : return NS_OK;
202 : }
203 :
204 : // static
205 : nsresult
206 0 : SnappyFrameUtils::ParseCompressedData(char* aDest, size_t aDestLength,
207 : const char* aData, size_t aDataLength,
208 : size_t* aBytesWrittenOut,
209 : size_t* aBytesReadOut)
210 : {
211 0 : *aBytesWrittenOut = 0;
212 0 : *aBytesReadOut = 0;
213 0 : size_t offset = 0;
214 :
215 0 : uint32_t readCrc = LittleEndian::readUint32(aData + offset);
216 0 : offset += kCRCLength;
217 :
218 : size_t uncompressedLength;
219 0 : if (NS_WARN_IF(!snappy::GetUncompressedLength(aData + offset,
220 : aDataLength - offset,
221 : &uncompressedLength))) {
222 0 : return NS_ERROR_CORRUPTED_CONTENT;
223 : }
224 :
225 0 : if (NS_WARN_IF(aDestLength < uncompressedLength)) {
226 0 : return NS_ERROR_NOT_AVAILABLE;
227 : }
228 :
229 0 : if (NS_WARN_IF(!snappy::RawUncompress(aData + offset, aDataLength - offset,
230 : aDest))) {
231 0 : return NS_ERROR_CORRUPTED_CONTENT;
232 : }
233 :
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 : }
240 :
241 0 : *aBytesWrittenOut = uncompressedLength;
242 0 : *aBytesReadOut = aDataLength;
243 :
244 0 : return NS_OK;
245 : }
246 :
247 : // static
248 : size_t
249 0 : SnappyFrameUtils::MaxCompressedBufferLength(size_t aSourceLength)
250 : {
251 0 : size_t neededLength = kHeaderLength;
252 0 : neededLength += kCRCLength;
253 0 : neededLength += snappy::MaxCompressedLength(aSourceLength);
254 0 : return neededLength;
255 : }
256 :
257 : } // namespace detail
258 : } // namespace mozilla
|