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::detail::SnappyFrameUtils
;
17 using mozilla::NativeEndian
;
19 SnappyFrameUtils::ChunkType
ReadChunkType(uint8_t aByte
)
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
) {
39 } else if (aType
== SnappyFrameUtils::CompressedData
) {
41 } else if (aType
== SnappyFrameUtils::UncompressedData
) {
43 } else if (aType
== SnappyFrameUtils::Padding
) {
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
)
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;
75 using mozilla::LittleEndian
;
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
90 aDest
[4] = 0x73; // "sNaPpY"
97 static_assert(kHeaderLength
+ kStreamIdentifierDataLength
== 10,
98 "StreamIdentifier chunk should be exactly 10 bytes long");
99 *aBytesWrittenOut
= kHeaderLength
+ kStreamIdentifierDataLength
;
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
;
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
),
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
;
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
);
161 SnappyFrameUtils::ParseData(char* aDest
, size_t aDestLength
,
162 ChunkType aType
, const char* aData
,
164 size_t* aBytesWrittenOut
, size_t* aBytesReadOut
)
167 case StreamIdentifier
:
168 return ParseStreamIdentifier(aDest
, aDestLength
, aData
, aDataLength
,
169 aBytesWrittenOut
, aBytesReadOut
);
172 return ParseCompressedData(aDest
, aDestLength
, aData
, aDataLength
,
173 aBytesWrittenOut
, aBytesReadOut
);
175 // TODO: support other snappy chunk types
177 MOZ_ASSERT_UNREACHABLE("Unsupported snappy framing chunk type.");
178 return NS_ERROR_NOT_IMPLEMENTED
;
184 SnappyFrameUtils::ParseStreamIdentifier(char*, size_t,
185 const char* aData
, size_t aDataLength
,
186 size_t* aBytesWrittenOut
,
187 size_t* aBytesReadOut
)
189 *aBytesWrittenOut
= 0;
191 if (NS_WARN_IF(aDataLength
!= kStreamIdentifierDataLength
||
198 return NS_ERROR_CORRUPTED_CONTENT
;
200 *aBytesReadOut
= aDataLength
;
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;
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
,
231 return NS_ERROR_CORRUPTED_CONTENT
;
234 uint32_t crc
= ComputeCrc32c(~0, reinterpret_cast<const unsigned char*>(aDest
),
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
;
249 SnappyFrameUtils::MaxCompressedBufferLength(size_t aSourceLength
)
251 size_t neededLength
= kHeaderLength
;
252 neededLength
+= kCRCLength
;
253 neededLength
+= snappy::MaxCompressedLength(aSourceLength
);
257 } // namespace detail
258 } // namespace mozilla