Bug 1932613 - temporarily disable browser_ml_end_to_end.js for permanent failures...
[gecko.git] / xpcom / io / SnappyFrameUtils.cpp
blobc606794fd90f5c365f1a59738fa4711feeb4d418
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::NativeEndian;
17 using mozilla::detail::SnappyFrameUtils;
19 SnappyFrameUtils::ChunkType ReadChunkType(uint8_t aByte) {
20 if (aByte == 0xff) {
21 return SnappyFrameUtils::StreamIdentifier;
22 } else if (aByte == 0x00) {
23 return SnappyFrameUtils::CompressedData;
24 } else if (aByte == 0x01) {
25 return SnappyFrameUtils::UncompressedData;
26 } else if (aByte == 0xfe) {
27 return SnappyFrameUtils::Padding;
30 return SnappyFrameUtils::Reserved;
33 void WriteChunkType(char* aDest, SnappyFrameUtils::ChunkType aType) {
34 unsigned char* dest = reinterpret_cast<unsigned char*>(aDest);
35 if (aType == SnappyFrameUtils::StreamIdentifier) {
36 *dest = 0xff;
37 } else if (aType == SnappyFrameUtils::CompressedData) {
38 *dest = 0x00;
39 } else if (aType == SnappyFrameUtils::UncompressedData) {
40 *dest = 0x01;
41 } else if (aType == SnappyFrameUtils::Padding) {
42 *dest = 0xfe;
43 } else {
44 *dest = 0x02;
48 void WriteUInt24(char* aBuf, uint32_t aVal) {
49 MOZ_ASSERT(!(aVal & 0xff000000));
50 uint32_t tmp = NativeEndian::swapToLittleEndian(aVal);
51 memcpy(aBuf, &tmp, 3);
54 uint32_t ReadUInt24(const char* aBuf) {
55 uint32_t val = 0;
56 memcpy(&val, aBuf, 3);
57 return NativeEndian::swapFromLittleEndian(val);
60 // This mask is explicitly defined in the snappy framing_format.txt file.
61 uint32_t MaskChecksum(uint32_t aValue) {
62 return ((aValue >> 15) | (aValue << 17)) + 0xa282ead8;
65 } // namespace
67 namespace mozilla {
68 namespace detail {
70 using mozilla::LittleEndian;
72 // static
73 nsresult SnappyFrameUtils::WriteStreamIdentifier(char* aDest,
74 size_t aDestLength,
75 size_t* aBytesWrittenOut) {
76 if (NS_WARN_IF(aDestLength < (kHeaderLength + kStreamIdentifierDataLength))) {
77 return NS_ERROR_NOT_AVAILABLE;
80 WriteChunkType(aDest, StreamIdentifier);
81 aDest[1] = 0x06; // Data length
82 aDest[2] = 0x00;
83 aDest[3] = 0x00;
84 aDest[4] = 0x73; // "sNaPpY"
85 aDest[5] = 0x4e;
86 aDest[6] = 0x61;
87 aDest[7] = 0x50;
88 aDest[8] = 0x70;
89 aDest[9] = 0x59;
91 static_assert(kHeaderLength + kStreamIdentifierDataLength == 10,
92 "StreamIdentifier chunk should be exactly 10 bytes long");
93 *aBytesWrittenOut = kHeaderLength + kStreamIdentifierDataLength;
95 return NS_OK;
98 // static
99 nsresult SnappyFrameUtils::WriteCompressedData(char* aDest, size_t aDestLength,
100 const char* aData,
101 size_t aDataLength,
102 size_t* aBytesWrittenOut) {
103 *aBytesWrittenOut = 0;
105 size_t neededLength = MaxCompressedBufferLength(aDataLength);
106 if (NS_WARN_IF(aDestLength < neededLength)) {
107 return NS_ERROR_NOT_AVAILABLE;
110 size_t offset = 0;
112 WriteChunkType(aDest, CompressedData);
113 offset += kChunkTypeLength;
115 // Skip length for now and write it out after we know the compressed length.
116 size_t lengthOffset = offset;
117 offset += kChunkLengthLength;
119 uint32_t crc = ComputeCrc32c(
120 ~0, reinterpret_cast<const unsigned char*>(aData), aDataLength);
121 uint32_t maskedCrc = MaskChecksum(crc);
122 LittleEndian::writeUint32(aDest + offset, maskedCrc);
123 offset += kCRCLength;
125 size_t compressedLength;
126 snappy::RawCompress(aData, aDataLength, aDest + offset, &compressedLength);
128 // Go back and write the data length.
129 size_t dataLength = compressedLength + kCRCLength;
130 WriteUInt24(aDest + lengthOffset, dataLength);
132 *aBytesWrittenOut = kHeaderLength + dataLength;
134 return NS_OK;
137 // static
138 nsresult SnappyFrameUtils::ParseHeader(const char* aSource,
139 size_t aSourceLength,
140 ChunkType* aTypeOut,
141 size_t* aDataLengthOut) {
142 if (NS_WARN_IF(aSourceLength < kHeaderLength)) {
143 return NS_ERROR_NOT_AVAILABLE;
146 *aTypeOut = ReadChunkType(aSource[0]);
147 *aDataLengthOut = ReadUInt24(aSource + kChunkTypeLength);
149 return NS_OK;
152 // static
153 nsresult SnappyFrameUtils::ParseData(char* aDest, size_t aDestLength,
154 ChunkType aType, const char* aData,
155 size_t aDataLength,
156 size_t* aBytesWrittenOut,
157 size_t* aBytesReadOut) {
158 switch (aType) {
159 case StreamIdentifier:
160 return ParseStreamIdentifier(aDest, aDestLength, aData, aDataLength,
161 aBytesWrittenOut, aBytesReadOut);
163 case CompressedData:
164 return ParseCompressedData(aDest, aDestLength, aData, aDataLength,
165 aBytesWrittenOut, aBytesReadOut);
167 // TODO: support other snappy chunk types
168 default:
169 MOZ_ASSERT_UNREACHABLE("Unsupported snappy framing chunk type.");
170 return NS_ERROR_NOT_IMPLEMENTED;
174 // static
175 nsresult SnappyFrameUtils::ParseStreamIdentifier(char*, size_t,
176 const char* aData,
177 size_t aDataLength,
178 size_t* aBytesWrittenOut,
179 size_t* aBytesReadOut) {
180 *aBytesWrittenOut = 0;
181 *aBytesReadOut = 0;
182 if (NS_WARN_IF(aDataLength != kStreamIdentifierDataLength ||
183 aData[0] != 0x73 || aData[1] != 0x4e || aData[2] != 0x61 ||
184 aData[3] != 0x50 || aData[4] != 0x70 || aData[5] != 0x59)) {
185 return NS_ERROR_CORRUPTED_CONTENT;
187 *aBytesReadOut = aDataLength;
188 return NS_OK;
191 // static
192 nsresult SnappyFrameUtils::ParseCompressedData(char* aDest, size_t aDestLength,
193 const char* aData,
194 size_t aDataLength,
195 size_t* aBytesWrittenOut,
196 size_t* aBytesReadOut) {
197 *aBytesWrittenOut = 0;
198 *aBytesReadOut = 0;
199 size_t offset = 0;
201 uint32_t readCrc = LittleEndian::readUint32(aData + offset);
202 offset += kCRCLength;
204 size_t uncompressedLength;
205 if (NS_WARN_IF(!snappy::GetUncompressedLength(
206 aData + offset, aDataLength - offset, &uncompressedLength))) {
207 return NS_ERROR_CORRUPTED_CONTENT;
210 if (NS_WARN_IF(aDestLength < uncompressedLength)) {
211 return NS_ERROR_NOT_AVAILABLE;
214 if (NS_WARN_IF(!snappy::RawUncompress(aData + offset, aDataLength - offset,
215 aDest))) {
216 return NS_ERROR_CORRUPTED_CONTENT;
219 uint32_t crc = ComputeCrc32c(
220 ~0, reinterpret_cast<const unsigned char*>(aDest), uncompressedLength);
221 uint32_t maskedCrc = MaskChecksum(crc);
222 if (NS_WARN_IF(readCrc != maskedCrc)) {
223 return NS_ERROR_CORRUPTED_CONTENT;
226 *aBytesWrittenOut = uncompressedLength;
227 *aBytesReadOut = aDataLength;
229 return NS_OK;
232 // static
233 size_t SnappyFrameUtils::MaxCompressedBufferLength(size_t aSourceLength) {
234 size_t neededLength = kHeaderLength;
235 neededLength += kCRCLength;
236 neededLength += snappy::MaxCompressedLength(aSourceLength);
237 return neededLength;
240 } // namespace detail
241 } // namespace mozilla