Bug 1431441 - Part 6 - Start middleman WebReplay process sandbox later r=Alex_Gaynor
[gecko.git] / xpcom / io / SnappyFrameUtils.cpp
blob97883a3629032a6c33ea01c4601f0a68d781ae82
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/SnappyFrameUtils.h"
9 #include "crc32c.h"
10 #include "mozilla/EndianUtils.h"
11 #include "nsDebug.h"
12 #include "snappy/snappy.h"
14 namespace {
16 using mozilla::detail::SnappyFrameUtils;
17 using mozilla::NativeEndian;
19 SnappyFrameUtils::ChunkType ReadChunkType(uint8_t aByte)
21 if (aByte == 0xff) {
22 return SnappyFrameUtils::StreamIdentifier;
23 } else if (aByte == 0x00) {
24 return SnappyFrameUtils::CompressedData;
25 } else if (aByte == 0x01) {
26 return SnappyFrameUtils::UncompressedData;
27 } else if (aByte == 0xfe) {
28 return SnappyFrameUtils::Padding;
31 return SnappyFrameUtils::Reserved;
34 void WriteChunkType(char* aDest, SnappyFrameUtils::ChunkType aType)
36 unsigned char* dest = reinterpret_cast<unsigned char*>(aDest);
37 if (aType == SnappyFrameUtils::StreamIdentifier) {
38 *dest = 0xff;
39 } else if (aType == SnappyFrameUtils::CompressedData) {
40 *dest = 0x00;
41 } else if (aType == SnappyFrameUtils::UncompressedData) {
42 *dest = 0x01;
43 } else if (aType == SnappyFrameUtils::Padding) {
44 *dest = 0xfe;
45 } else {
46 *dest = 0x02;
50 void WriteUInt24(char* aBuf, uint32_t aVal)
52 MOZ_ASSERT(!(aVal & 0xff000000));
53 uint32_t tmp = NativeEndian::swapToLittleEndian(aVal);
54 memcpy(aBuf, &tmp, 3);
57 uint32_t ReadUInt24(const char* aBuf)
59 uint32_t val = 0;
60 memcpy(&val, aBuf, 3);
61 return NativeEndian::swapFromLittleEndian(val);
64 // This mask is explicitly defined in the snappy framing_format.txt file.
65 uint32_t MaskChecksum(uint32_t aValue)
67 return ((aValue >> 15) | (aValue << 17)) + 0xa282ead8;
70 } // namespace
72 namespace mozilla {
73 namespace detail {
75 using mozilla::LittleEndian;
77 // static
78 nsresult
79 SnappyFrameUtils::WriteStreamIdentifier(char* aDest, size_t aDestLength,
80 size_t* aBytesWrittenOut)
82 if (NS_WARN_IF(aDestLength < (kHeaderLength + kStreamIdentifierDataLength))) {
83 return NS_ERROR_NOT_AVAILABLE;
86 WriteChunkType(aDest, StreamIdentifier);
87 aDest[1] = 0x06; // Data length
88 aDest[2] = 0x00;
89 aDest[3] = 0x00;
90 aDest[4] = 0x73; // "sNaPpY"
91 aDest[5] = 0x4e;
92 aDest[6] = 0x61;
93 aDest[7] = 0x50;
94 aDest[8] = 0x70;
95 aDest[9] = 0x59;
97 static_assert(kHeaderLength + kStreamIdentifierDataLength == 10,
98 "StreamIdentifier chunk should be exactly 10 bytes long");
99 *aBytesWrittenOut = kHeaderLength + kStreamIdentifierDataLength;
101 return NS_OK;
104 // static
105 nsresult
106 SnappyFrameUtils::WriteCompressedData(char* aDest, size_t aDestLength,
107 const char* aData, size_t aDataLength,
108 size_t* aBytesWrittenOut)
110 *aBytesWrittenOut = 0;
112 size_t neededLength = MaxCompressedBufferLength(aDataLength);
113 if (NS_WARN_IF(aDestLength < neededLength)) {
114 return NS_ERROR_NOT_AVAILABLE;
117 size_t offset = 0;
119 WriteChunkType(aDest, CompressedData);
120 offset += kChunkTypeLength;
122 // Skip length for now and write it out after we know the compressed length.
123 size_t lengthOffset = offset;
124 offset += kChunkLengthLength;
126 uint32_t crc = ComputeCrc32c(~0, reinterpret_cast<const unsigned char*>(aData),
127 aDataLength);
128 uint32_t maskedCrc = MaskChecksum(crc);
129 LittleEndian::writeUint32(aDest + offset, maskedCrc);
130 offset += kCRCLength;
132 size_t compressedLength;
133 snappy::RawCompress(aData, aDataLength, aDest + offset, &compressedLength);
135 // Go back and write the data length.
136 size_t dataLength = compressedLength + kCRCLength;
137 WriteUInt24(aDest + lengthOffset, dataLength);
139 *aBytesWrittenOut = kHeaderLength + dataLength;
141 return NS_OK;
144 // static
145 nsresult
146 SnappyFrameUtils::ParseHeader(const char* aSource, size_t aSourceLength,
147 ChunkType* aTypeOut, size_t* aDataLengthOut)
149 if (NS_WARN_IF(aSourceLength < kHeaderLength)) {
150 return NS_ERROR_NOT_AVAILABLE;
153 *aTypeOut = ReadChunkType(aSource[0]);
154 *aDataLengthOut = ReadUInt24(aSource + kChunkTypeLength);
156 return NS_OK;
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)
166 switch(aType) {
167 case StreamIdentifier:
168 return ParseStreamIdentifier(aDest, aDestLength, aData, aDataLength,
169 aBytesWrittenOut, aBytesReadOut);
171 case CompressedData:
172 return ParseCompressedData(aDest, aDestLength, aData, aDataLength,
173 aBytesWrittenOut, aBytesReadOut);
175 // TODO: support other snappy chunk types
176 default:
177 MOZ_ASSERT_UNREACHABLE("Unsupported snappy framing chunk type.");
178 return NS_ERROR_NOT_IMPLEMENTED;
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)
189 *aBytesWrittenOut = 0;
190 *aBytesReadOut = 0;
191 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 return NS_ERROR_CORRUPTED_CONTENT;
200 *aBytesReadOut = aDataLength;
201 return NS_OK;
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)
211 *aBytesWrittenOut = 0;
212 *aBytesReadOut = 0;
213 size_t offset = 0;
215 uint32_t readCrc = LittleEndian::readUint32(aData + offset);
216 offset += kCRCLength;
218 size_t uncompressedLength;
219 if (NS_WARN_IF(!snappy::GetUncompressedLength(aData + offset,
220 aDataLength - offset,
221 &uncompressedLength))) {
222 return NS_ERROR_CORRUPTED_CONTENT;
225 if (NS_WARN_IF(aDestLength < uncompressedLength)) {
226 return NS_ERROR_NOT_AVAILABLE;
229 if (NS_WARN_IF(!snappy::RawUncompress(aData + offset, aDataLength - offset,
230 aDest))) {
231 return NS_ERROR_CORRUPTED_CONTENT;
234 uint32_t crc = ComputeCrc32c(~0, reinterpret_cast<const unsigned char*>(aDest),
235 uncompressedLength);
236 uint32_t maskedCrc = MaskChecksum(crc);
237 if (NS_WARN_IF(readCrc != maskedCrc)) {
238 return NS_ERROR_CORRUPTED_CONTENT;
241 *aBytesWrittenOut = uncompressedLength;
242 *aBytesReadOut = aDataLength;
244 return NS_OK;
247 // static
248 size_t
249 SnappyFrameUtils::MaxCompressedBufferLength(size_t aSourceLength)
251 size_t neededLength = kHeaderLength;
252 neededLength += kCRCLength;
253 neededLength += snappy::MaxCompressedLength(aSourceLength);
254 return neededLength;
257 } // namespace detail
258 } // namespace mozilla