std::unique_ptr<IOBuf> LZ4Codec::doUncompress( const IOBuf* data, uint64_t uncompressedLength) { std::unique_ptr<IOBuf> clone; if (data->isChained()) { // LZ4 doesn't support streaming, so we have to coalesce clone = data->clone(); clone->coalesce(); data = clone.get(); } folly::io::Cursor cursor(data); uint64_t actualUncompressedLength; if (encodeSize()) { actualUncompressedLength = decodeVarintFromCursor(cursor); if (uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH && uncompressedLength != actualUncompressedLength) { throw std::runtime_error("LZ4Codec: invalid uncompressed length"); } } else { actualUncompressedLength = uncompressedLength; if (actualUncompressedLength == UNKNOWN_UNCOMPRESSED_LENGTH || actualUncompressedLength > maxUncompressedLength()) { throw std::runtime_error("LZ4Codec: invalid uncompressed length"); } } auto sp = StringPiece{cursor.peekBytes()}; auto out = IOBuf::create(actualUncompressedLength); int n = LZ4_decompress_safe( sp.data(), reinterpret_cast<char*>(out->writableTail()), sp.size(), actualUncompressedLength); if (n < 0 || uint64_t(n) != actualUncompressedLength) { throw std::runtime_error(to<std::string>( "LZ4 decompression returned invalid value ", n)); } out->append(actualUncompressedLength); return out; }
void encode(const LuaObject& input, folly::io::CodecType codecType, LuaVersionInfo versionInfo, Writer&& writer, int maxVersion, uint64_t chunkLength) { folly::IOBufQueue dataQueue(folly::IOBufQueue::cacheChainLength()); apache::thrift::CompactSerializer::serialize(input, &dataQueue); auto codec = folly::io::getCodec(codecType); // Determine minimum version required for reading bool needChunking = false; uint64_t codecMaxLength = codec->maxUncompressedLength(); if (codecMaxLength < chunkLength) { chunkLength = codecMaxLength; } int version = 0; if (dataQueue.chainLength() > chunkLength) { needChunking = true; // Version 2: chunking version = 2; } else { // Version 1: specials, metatables for (auto& ref : input.refs) { if (ref.__isset.tableVal) { auto& table = ref.tableVal; if (table.__isset.specialKey || table.__isset.specialValue || table.__isset.metatable) { version = 1; break; } } } } DCHECK_LE(version, kMaxSupportedVersion); if (version > maxVersion) { throw std::invalid_argument(folly::to<std::string>( "Version ", version, " required (requested ", maxVersion, ")")); } ThriftHeader th; th.version = version; th.codec = static_cast<int32_t>(codecType); th.uncompressedLength = dataQueue.chainLength(); th.luaVersionInfo = std::move(versionInfo); auto uncompressed = dataQueue.move(); std::unique_ptr<folly::IOBuf> compressed; if (needChunking) { th.__isset.chunks = true; compressed = compressChunked( codec.get(), uncompressed.get(), chunkLength, th.chunks); } else { compressed = codec->compress(uncompressed.get()); } th.compressedLength = compressed->computeChainDataLength(); folly::IOBufQueue queue(folly::IOBufQueue::cacheChainLength()); apache::thrift::CompactSerializer::serialize(th, &queue); Header header; header.magic = folly::Endian::little(kMagic); header.thriftHeaderLength = folly::Endian::little(queue.chainLength()); writer(folly::IOBuf::copyBuffer(&header, sizeof(header))); writer(queue.move()); writer(std::move(compressed)); }