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"
10 #include "mozilla/EndianUtils.h"
12 #include "snappy/snappy.h"
16 using mozilla::NativeEndian
;
17 using mozilla::detail::SnappyFrameUtils
;
19 SnappyFrameUtils::ChunkType
ReadChunkType(uint8_t aByte
) {
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
) {
37 } else if (aType
== SnappyFrameUtils::CompressedData
) {
39 } else if (aType
== SnappyFrameUtils::UncompressedData
) {
41 } else if (aType
== SnappyFrameUtils::Padding
) {
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
) {
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;
70 using mozilla::LittleEndian
;
73 nsresult
SnappyFrameUtils::WriteStreamIdentifier(char* aDest
,
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
84 aDest
[4] = 0x73; // "sNaPpY"
91 static_assert(kHeaderLength
+ kStreamIdentifierDataLength
== 10,
92 "StreamIdentifier chunk should be exactly 10 bytes long");
93 *aBytesWrittenOut
= kHeaderLength
+ kStreamIdentifierDataLength
;
99 nsresult
SnappyFrameUtils::WriteCompressedData(char* aDest
, size_t aDestLength
,
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
;
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
;
138 nsresult
SnappyFrameUtils::ParseHeader(const char* aSource
,
139 size_t aSourceLength
,
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
);
153 nsresult
SnappyFrameUtils::ParseData(char* aDest
, size_t aDestLength
,
154 ChunkType aType
, const char* aData
,
156 size_t* aBytesWrittenOut
,
157 size_t* aBytesReadOut
) {
159 case StreamIdentifier
:
160 return ParseStreamIdentifier(aDest
, aDestLength
, aData
, aDataLength
,
161 aBytesWrittenOut
, aBytesReadOut
);
164 return ParseCompressedData(aDest
, aDestLength
, aData
, aDataLength
,
165 aBytesWrittenOut
, aBytesReadOut
);
167 // TODO: support other snappy chunk types
169 MOZ_ASSERT_UNREACHABLE("Unsupported snappy framing chunk type.");
170 return NS_ERROR_NOT_IMPLEMENTED
;
175 nsresult
SnappyFrameUtils::ParseStreamIdentifier(char*, size_t,
178 size_t* aBytesWrittenOut
,
179 size_t* aBytesReadOut
) {
180 *aBytesWrittenOut
= 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
;
192 nsresult
SnappyFrameUtils::ParseCompressedData(char* aDest
, size_t aDestLength
,
195 size_t* aBytesWrittenOut
,
196 size_t* aBytesReadOut
) {
197 *aBytesWrittenOut
= 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
,
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
;
233 size_t SnappyFrameUtils::MaxCompressedBufferLength(size_t aSourceLength
) {
234 size_t neededLength
= kHeaderLength
;
235 neededLength
+= kCRCLength
;
236 neededLength
+= snappy::MaxCompressedLength(aSourceLength
);
240 } // namespace detail
241 } // namespace mozilla