mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
265e672179
# ignore-this-changeset --HG-- extra : amend_source : 4d301d3b0b8711c4692392aa76088ba7fd7d1022
242 lines
7.5 KiB
C++
242 lines
7.5 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/SnappyFrameUtils.h"
|
|
|
|
#include "crc32c.h"
|
|
#include "mozilla/EndianUtils.h"
|
|
#include "nsDebug.h"
|
|
#include "snappy/snappy.h"
|
|
|
|
namespace {
|
|
|
|
using mozilla::NativeEndian;
|
|
using mozilla::detail::SnappyFrameUtils;
|
|
|
|
SnappyFrameUtils::ChunkType ReadChunkType(uint8_t aByte) {
|
|
if (aByte == 0xff) {
|
|
return SnappyFrameUtils::StreamIdentifier;
|
|
} else if (aByte == 0x00) {
|
|
return SnappyFrameUtils::CompressedData;
|
|
} else if (aByte == 0x01) {
|
|
return SnappyFrameUtils::UncompressedData;
|
|
} else if (aByte == 0xfe) {
|
|
return SnappyFrameUtils::Padding;
|
|
}
|
|
|
|
return SnappyFrameUtils::Reserved;
|
|
}
|
|
|
|
void WriteChunkType(char* aDest, SnappyFrameUtils::ChunkType aType) {
|
|
unsigned char* dest = reinterpret_cast<unsigned char*>(aDest);
|
|
if (aType == SnappyFrameUtils::StreamIdentifier) {
|
|
*dest = 0xff;
|
|
} else if (aType == SnappyFrameUtils::CompressedData) {
|
|
*dest = 0x00;
|
|
} else if (aType == SnappyFrameUtils::UncompressedData) {
|
|
*dest = 0x01;
|
|
} else if (aType == SnappyFrameUtils::Padding) {
|
|
*dest = 0xfe;
|
|
} else {
|
|
*dest = 0x02;
|
|
}
|
|
}
|
|
|
|
void WriteUInt24(char* aBuf, uint32_t aVal) {
|
|
MOZ_ASSERT(!(aVal & 0xff000000));
|
|
uint32_t tmp = NativeEndian::swapToLittleEndian(aVal);
|
|
memcpy(aBuf, &tmp, 3);
|
|
}
|
|
|
|
uint32_t ReadUInt24(const char* aBuf) {
|
|
uint32_t val = 0;
|
|
memcpy(&val, aBuf, 3);
|
|
return NativeEndian::swapFromLittleEndian(val);
|
|
}
|
|
|
|
// This mask is explicitly defined in the snappy framing_format.txt file.
|
|
uint32_t MaskChecksum(uint32_t aValue) {
|
|
return ((aValue >> 15) | (aValue << 17)) + 0xa282ead8;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace mozilla {
|
|
namespace detail {
|
|
|
|
using mozilla::LittleEndian;
|
|
|
|
// static
|
|
nsresult SnappyFrameUtils::WriteStreamIdentifier(char* aDest,
|
|
size_t aDestLength,
|
|
size_t* aBytesWrittenOut) {
|
|
if (NS_WARN_IF(aDestLength < (kHeaderLength + kStreamIdentifierDataLength))) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
WriteChunkType(aDest, StreamIdentifier);
|
|
aDest[1] = 0x06; // Data length
|
|
aDest[2] = 0x00;
|
|
aDest[3] = 0x00;
|
|
aDest[4] = 0x73; // "sNaPpY"
|
|
aDest[5] = 0x4e;
|
|
aDest[6] = 0x61;
|
|
aDest[7] = 0x50;
|
|
aDest[8] = 0x70;
|
|
aDest[9] = 0x59;
|
|
|
|
static_assert(kHeaderLength + kStreamIdentifierDataLength == 10,
|
|
"StreamIdentifier chunk should be exactly 10 bytes long");
|
|
*aBytesWrittenOut = kHeaderLength + kStreamIdentifierDataLength;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// static
|
|
nsresult SnappyFrameUtils::WriteCompressedData(char* aDest, size_t aDestLength,
|
|
const char* aData,
|
|
size_t aDataLength,
|
|
size_t* aBytesWrittenOut) {
|
|
*aBytesWrittenOut = 0;
|
|
|
|
size_t neededLength = MaxCompressedBufferLength(aDataLength);
|
|
if (NS_WARN_IF(aDestLength < neededLength)) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
size_t offset = 0;
|
|
|
|
WriteChunkType(aDest, CompressedData);
|
|
offset += kChunkTypeLength;
|
|
|
|
// Skip length for now and write it out after we know the compressed length.
|
|
size_t lengthOffset = offset;
|
|
offset += kChunkLengthLength;
|
|
|
|
uint32_t crc = ComputeCrc32c(
|
|
~0, reinterpret_cast<const unsigned char*>(aData), aDataLength);
|
|
uint32_t maskedCrc = MaskChecksum(crc);
|
|
LittleEndian::writeUint32(aDest + offset, maskedCrc);
|
|
offset += kCRCLength;
|
|
|
|
size_t compressedLength;
|
|
snappy::RawCompress(aData, aDataLength, aDest + offset, &compressedLength);
|
|
|
|
// Go back and write the data length.
|
|
size_t dataLength = compressedLength + kCRCLength;
|
|
WriteUInt24(aDest + lengthOffset, dataLength);
|
|
|
|
*aBytesWrittenOut = kHeaderLength + dataLength;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// static
|
|
nsresult SnappyFrameUtils::ParseHeader(const char* aSource,
|
|
size_t aSourceLength,
|
|
ChunkType* aTypeOut,
|
|
size_t* aDataLengthOut) {
|
|
if (NS_WARN_IF(aSourceLength < kHeaderLength)) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
*aTypeOut = ReadChunkType(aSource[0]);
|
|
*aDataLengthOut = ReadUInt24(aSource + kChunkTypeLength);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// static
|
|
nsresult SnappyFrameUtils::ParseData(char* aDest, size_t aDestLength,
|
|
ChunkType aType, const char* aData,
|
|
size_t aDataLength,
|
|
size_t* aBytesWrittenOut,
|
|
size_t* aBytesReadOut) {
|
|
switch (aType) {
|
|
case StreamIdentifier:
|
|
return ParseStreamIdentifier(aDest, aDestLength, aData, aDataLength,
|
|
aBytesWrittenOut, aBytesReadOut);
|
|
|
|
case CompressedData:
|
|
return ParseCompressedData(aDest, aDestLength, aData, aDataLength,
|
|
aBytesWrittenOut, aBytesReadOut);
|
|
|
|
// TODO: support other snappy chunk types
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("Unsupported snappy framing chunk type.");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
}
|
|
|
|
// static
|
|
nsresult SnappyFrameUtils::ParseStreamIdentifier(char*, size_t,
|
|
const char* aData,
|
|
size_t aDataLength,
|
|
size_t* aBytesWrittenOut,
|
|
size_t* aBytesReadOut) {
|
|
*aBytesWrittenOut = 0;
|
|
*aBytesReadOut = 0;
|
|
if (NS_WARN_IF(aDataLength != kStreamIdentifierDataLength ||
|
|
aData[0] != 0x73 || aData[1] != 0x4e || aData[2] != 0x61 ||
|
|
aData[3] != 0x50 || aData[4] != 0x70 || aData[5] != 0x59)) {
|
|
return NS_ERROR_CORRUPTED_CONTENT;
|
|
}
|
|
*aBytesReadOut = aDataLength;
|
|
return NS_OK;
|
|
}
|
|
|
|
// static
|
|
nsresult SnappyFrameUtils::ParseCompressedData(char* aDest, size_t aDestLength,
|
|
const char* aData,
|
|
size_t aDataLength,
|
|
size_t* aBytesWrittenOut,
|
|
size_t* aBytesReadOut) {
|
|
*aBytesWrittenOut = 0;
|
|
*aBytesReadOut = 0;
|
|
size_t offset = 0;
|
|
|
|
uint32_t readCrc = LittleEndian::readUint32(aData + offset);
|
|
offset += kCRCLength;
|
|
|
|
size_t uncompressedLength;
|
|
if (NS_WARN_IF(!snappy::GetUncompressedLength(
|
|
aData + offset, aDataLength - offset, &uncompressedLength))) {
|
|
return NS_ERROR_CORRUPTED_CONTENT;
|
|
}
|
|
|
|
if (NS_WARN_IF(aDestLength < uncompressedLength)) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
if (NS_WARN_IF(!snappy::RawUncompress(aData + offset, aDataLength - offset,
|
|
aDest))) {
|
|
return NS_ERROR_CORRUPTED_CONTENT;
|
|
}
|
|
|
|
uint32_t crc = ComputeCrc32c(
|
|
~0, reinterpret_cast<const unsigned char*>(aDest), uncompressedLength);
|
|
uint32_t maskedCrc = MaskChecksum(crc);
|
|
if (NS_WARN_IF(readCrc != maskedCrc)) {
|
|
return NS_ERROR_CORRUPTED_CONTENT;
|
|
}
|
|
|
|
*aBytesWrittenOut = uncompressedLength;
|
|
*aBytesReadOut = aDataLength;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// static
|
|
size_t SnappyFrameUtils::MaxCompressedBufferLength(size_t aSourceLength) {
|
|
size_t neededLength = kHeaderLength;
|
|
neededLength += kCRCLength;
|
|
neededLength += snappy::MaxCompressedLength(aSourceLength);
|
|
return neededLength;
|
|
}
|
|
|
|
} // namespace detail
|
|
} // namespace mozilla
|