2 * Copyright 2014 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <folly/io/Compression.h>
24 #include <glog/logging.h>
26 #if FOLLY_HAVE_LIBSNAPPY
28 #include <snappy-sinksource.h>
35 #if FOLLY_HAVE_LIBLZMA
39 #include <folly/Conv.h>
40 #include <folly/Memory.h>
41 #include <folly/Portability.h>
42 #include <folly/ScopeGuard.h>
43 #include <folly/Varint.h>
44 #include <folly/io/Cursor.h>
46 namespace folly { namespace io {
48 Codec::Codec(CodecType type) : type_(type) { }
50 // Ensure consistent behavior in the nullptr case
51 std::unique_ptr<IOBuf> Codec::compress(const IOBuf* data) {
52 uint64_t len = data->computeChainDataLength();
54 return IOBuf::create(0);
55 } else if (len > maxUncompressedLength()) {
56 throw std::runtime_error("Codec: uncompressed length too large");
59 return doCompress(data);
62 std::unique_ptr<IOBuf> Codec::uncompress(const IOBuf* data,
63 uint64_t uncompressedLength) {
64 if (uncompressedLength == UNKNOWN_UNCOMPRESSED_LENGTH) {
65 if (needsUncompressedLength()) {
66 throw std::invalid_argument("Codec: uncompressed length required");
68 } else if (uncompressedLength > maxUncompressedLength()) {
69 throw std::runtime_error("Codec: uncompressed length too large");
73 if (uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
74 uncompressedLength != 0) {
75 throw std::runtime_error("Codec: invalid uncompressed length");
77 return IOBuf::create(0);
80 return doUncompress(data, uncompressedLength);
83 bool Codec::needsUncompressedLength() const {
84 return doNeedsUncompressedLength();
87 uint64_t Codec::maxUncompressedLength() const {
88 return doMaxUncompressedLength();
91 bool Codec::doNeedsUncompressedLength() const {
95 uint64_t Codec::doMaxUncompressedLength() const {
96 return UNLIMITED_UNCOMPRESSED_LENGTH;
104 class NoCompressionCodec FOLLY_FINAL : public Codec {
106 static std::unique_ptr<Codec> create(int level, CodecType type);
107 explicit NoCompressionCodec(int level, CodecType type);
110 std::unique_ptr<IOBuf> doCompress(const IOBuf* data) FOLLY_OVERRIDE;
111 std::unique_ptr<IOBuf> doUncompress(
113 uint64_t uncompressedLength) FOLLY_OVERRIDE;
116 std::unique_ptr<Codec> NoCompressionCodec::create(int level, CodecType type) {
117 return make_unique<NoCompressionCodec>(level, type);
120 NoCompressionCodec::NoCompressionCodec(int level, CodecType type)
122 DCHECK(type == CodecType::NO_COMPRESSION);
124 case COMPRESSION_LEVEL_DEFAULT:
125 case COMPRESSION_LEVEL_FASTEST:
126 case COMPRESSION_LEVEL_BEST:
130 throw std::invalid_argument(to<std::string>(
131 "NoCompressionCodec: invalid level ", level));
135 std::unique_ptr<IOBuf> NoCompressionCodec::doCompress(
137 return data->clone();
140 std::unique_ptr<IOBuf> NoCompressionCodec::doUncompress(
142 uint64_t uncompressedLength) {
143 if (uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
144 data->computeChainDataLength() != uncompressedLength) {
145 throw std::runtime_error(to<std::string>(
146 "NoCompressionCodec: invalid uncompressed length"));
148 return data->clone();
151 #if (FOLLY_HAVE_LIBLZ4 || FOLLY_HAVE_LIBLZMA)
155 void encodeVarintToIOBuf(uint64_t val, folly::IOBuf* out) {
156 DCHECK_GE(out->tailroom(), kMaxVarintLength64);
157 out->append(encodeVarint(val, out->writableTail()));
160 inline uint64_t decodeVarintFromCursor(folly::io::Cursor& cursor) {
163 for (int shift = 0; shift <= 63; shift += 7) {
164 b = cursor.read<int8_t>();
165 val |= static_cast<uint64_t>(b & 0x7f) << shift;
171 throw std::invalid_argument("Invalid varint value. Too big.");
178 #endif // FOLLY_HAVE_LIBLZ4 || FOLLY_HAVE_LIBLZMA
180 #if FOLLY_HAVE_LIBLZ4
185 class LZ4Codec FOLLY_FINAL : public Codec {
187 static std::unique_ptr<Codec> create(int level, CodecType type);
188 explicit LZ4Codec(int level, CodecType type);
191 bool doNeedsUncompressedLength() const FOLLY_OVERRIDE;
192 uint64_t doMaxUncompressedLength() const FOLLY_OVERRIDE;
194 bool encodeSize() const { return type() == CodecType::LZ4_VARINT_SIZE; }
196 std::unique_ptr<IOBuf> doCompress(const IOBuf* data) FOLLY_OVERRIDE;
197 std::unique_ptr<IOBuf> doUncompress(
199 uint64_t uncompressedLength) FOLLY_OVERRIDE;
201 bool highCompression_;
204 std::unique_ptr<Codec> LZ4Codec::create(int level, CodecType type) {
205 return make_unique<LZ4Codec>(level, type);
208 LZ4Codec::LZ4Codec(int level, CodecType type) : Codec(type) {
209 DCHECK(type == CodecType::LZ4 || type == CodecType::LZ4_VARINT_SIZE);
212 case COMPRESSION_LEVEL_FASTEST:
213 case COMPRESSION_LEVEL_DEFAULT:
216 case COMPRESSION_LEVEL_BEST:
220 if (level < 1 || level > 2) {
221 throw std::invalid_argument(to<std::string>(
222 "LZ4Codec: invalid level: ", level));
224 highCompression_ = (level > 1);
227 bool LZ4Codec::doNeedsUncompressedLength() const {
228 return !encodeSize();
231 // The value comes from lz4.h in lz4-r117, but older versions of lz4 don't
232 // define LZ4_MAX_INPUT_SIZE (even though the max size is the same), so do it
234 #ifndef LZ4_MAX_INPUT_SIZE
235 # define LZ4_MAX_INPUT_SIZE 0x7E000000
238 uint64_t LZ4Codec::doMaxUncompressedLength() const {
239 return LZ4_MAX_INPUT_SIZE;
242 std::unique_ptr<IOBuf> LZ4Codec::doCompress(const IOBuf* data) {
243 std::unique_ptr<IOBuf> clone;
244 if (data->isChained()) {
245 // LZ4 doesn't support streaming, so we have to coalesce
246 clone = data->clone();
251 uint32_t extraSize = encodeSize() ? kMaxVarintLength64 : 0;
252 auto out = IOBuf::create(extraSize + LZ4_compressBound(data->length()));
254 encodeVarintToIOBuf(data->length(), out.get());
258 if (highCompression_) {
259 n = LZ4_compressHC(reinterpret_cast<const char*>(data->data()),
260 reinterpret_cast<char*>(out->writableTail()),
263 n = LZ4_compress(reinterpret_cast<const char*>(data->data()),
264 reinterpret_cast<char*>(out->writableTail()),
269 CHECK_LE(n, out->capacity());
275 std::unique_ptr<IOBuf> LZ4Codec::doUncompress(
277 uint64_t uncompressedLength) {
278 std::unique_ptr<IOBuf> clone;
279 if (data->isChained()) {
280 // LZ4 doesn't support streaming, so we have to coalesce
281 clone = data->clone();
286 folly::io::Cursor cursor(data);
287 uint64_t actualUncompressedLength;
289 actualUncompressedLength = decodeVarintFromCursor(cursor);
290 if (uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
291 uncompressedLength != actualUncompressedLength) {
292 throw std::runtime_error("LZ4Codec: invalid uncompressed length");
295 actualUncompressedLength = uncompressedLength;
296 if (actualUncompressedLength == UNKNOWN_UNCOMPRESSED_LENGTH ||
297 actualUncompressedLength > maxUncompressedLength()) {
298 throw std::runtime_error("LZ4Codec: invalid uncompressed length");
302 auto p = cursor.peek();
303 auto out = IOBuf::create(actualUncompressedLength);
304 int n = LZ4_decompress_safe(reinterpret_cast<const char*>(p.first),
305 reinterpret_cast<char*>(out->writableTail()),
307 actualUncompressedLength);
309 if (n < 0 || uint64_t(n) != actualUncompressedLength) {
310 throw std::runtime_error(to<std::string>(
311 "LZ4 decompression returned invalid value ", n));
313 out->append(actualUncompressedLength);
317 #endif // FOLLY_HAVE_LIBLZ4
319 #if FOLLY_HAVE_LIBSNAPPY
326 * Implementation of snappy::Source that reads from a IOBuf chain.
328 class IOBufSnappySource FOLLY_FINAL : public snappy::Source {
330 explicit IOBufSnappySource(const IOBuf* data);
331 size_t Available() const FOLLY_OVERRIDE;
332 const char* Peek(size_t* len) FOLLY_OVERRIDE;
333 void Skip(size_t n) FOLLY_OVERRIDE;
339 IOBufSnappySource::IOBufSnappySource(const IOBuf* data)
340 : available_(data->computeChainDataLength()),
344 size_t IOBufSnappySource::Available() const {
348 const char* IOBufSnappySource::Peek(size_t* len) {
349 auto p = cursor_.peek();
351 return reinterpret_cast<const char*>(p.first);
354 void IOBufSnappySource::Skip(size_t n) {
355 CHECK_LE(n, available_);
360 class SnappyCodec FOLLY_FINAL : public Codec {
362 static std::unique_ptr<Codec> create(int level, CodecType type);
363 explicit SnappyCodec(int level, CodecType type);
366 uint64_t doMaxUncompressedLength() const FOLLY_OVERRIDE;
367 std::unique_ptr<IOBuf> doCompress(const IOBuf* data) FOLLY_OVERRIDE;
368 std::unique_ptr<IOBuf> doUncompress(
370 uint64_t uncompressedLength) FOLLY_OVERRIDE;
373 std::unique_ptr<Codec> SnappyCodec::create(int level, CodecType type) {
374 return make_unique<SnappyCodec>(level, type);
377 SnappyCodec::SnappyCodec(int level, CodecType type) : Codec(type) {
378 DCHECK(type == CodecType::SNAPPY);
380 case COMPRESSION_LEVEL_FASTEST:
381 case COMPRESSION_LEVEL_DEFAULT:
382 case COMPRESSION_LEVEL_BEST:
386 throw std::invalid_argument(to<std::string>(
387 "SnappyCodec: invalid level: ", level));
391 uint64_t SnappyCodec::doMaxUncompressedLength() const {
392 // snappy.h uses uint32_t for lengths, so there's that.
393 return std::numeric_limits<uint32_t>::max();
396 std::unique_ptr<IOBuf> SnappyCodec::doCompress(const IOBuf* data) {
397 IOBufSnappySource source(data);
399 IOBuf::create(snappy::MaxCompressedLength(source.Available()));
401 snappy::UncheckedByteArraySink sink(reinterpret_cast<char*>(
402 out->writableTail()));
404 size_t n = snappy::Compress(&source, &sink);
406 CHECK_LE(n, out->capacity());
411 std::unique_ptr<IOBuf> SnappyCodec::doUncompress(const IOBuf* data,
412 uint64_t uncompressedLength) {
413 uint32_t actualUncompressedLength = 0;
416 IOBufSnappySource source(data);
417 if (!snappy::GetUncompressedLength(&source, &actualUncompressedLength)) {
418 throw std::runtime_error("snappy::GetUncompressedLength failed");
420 if (uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
421 uncompressedLength != actualUncompressedLength) {
422 throw std::runtime_error("snappy: invalid uncompressed length");
426 auto out = IOBuf::create(actualUncompressedLength);
429 IOBufSnappySource source(data);
430 if (!snappy::RawUncompress(&source,
431 reinterpret_cast<char*>(out->writableTail()))) {
432 throw std::runtime_error("snappy::RawUncompress failed");
436 out->append(actualUncompressedLength);
440 #endif // FOLLY_HAVE_LIBSNAPPY
446 class ZlibCodec FOLLY_FINAL : public Codec {
448 static std::unique_ptr<Codec> create(int level, CodecType type);
449 explicit ZlibCodec(int level, CodecType type);
452 std::unique_ptr<IOBuf> doCompress(const IOBuf* data) FOLLY_OVERRIDE;
453 std::unique_ptr<IOBuf> doUncompress(
455 uint64_t uncompressedLength) FOLLY_OVERRIDE;
457 std::unique_ptr<IOBuf> addOutputBuffer(z_stream* stream, uint32_t length);
458 bool doInflate(z_stream* stream, IOBuf* head, uint32_t bufferLength);
463 std::unique_ptr<Codec> ZlibCodec::create(int level, CodecType type) {
464 return make_unique<ZlibCodec>(level, type);
467 ZlibCodec::ZlibCodec(int level, CodecType type) : Codec(type) {
468 DCHECK(type == CodecType::ZLIB);
470 case COMPRESSION_LEVEL_FASTEST:
473 case COMPRESSION_LEVEL_DEFAULT:
474 level = Z_DEFAULT_COMPRESSION;
476 case COMPRESSION_LEVEL_BEST:
480 if (level != Z_DEFAULT_COMPRESSION && (level < 0 || level > 9)) {
481 throw std::invalid_argument(to<std::string>(
482 "ZlibCodec: invalid level: ", level));
487 std::unique_ptr<IOBuf> ZlibCodec::addOutputBuffer(z_stream* stream,
489 CHECK_EQ(stream->avail_out, 0);
491 auto buf = IOBuf::create(length);
494 stream->next_out = buf->writableData();
495 stream->avail_out = buf->length();
500 bool ZlibCodec::doInflate(z_stream* stream,
502 uint32_t bufferLength) {
503 if (stream->avail_out == 0) {
504 head->prependChain(addOutputBuffer(stream, bufferLength));
507 int rc = inflate(stream, Z_NO_FLUSH);
518 throw std::runtime_error(to<std::string>(
519 "ZlibCodec: inflate error: ", rc, ": ", stream->msg));
521 CHECK(false) << rc << ": " << stream->msg;
527 std::unique_ptr<IOBuf> ZlibCodec::doCompress(const IOBuf* data) {
529 stream.zalloc = nullptr;
530 stream.zfree = nullptr;
531 stream.opaque = nullptr;
533 int rc = deflateInit(&stream, level_);
535 throw std::runtime_error(to<std::string>(
536 "ZlibCodec: deflateInit error: ", rc, ": ", stream.msg));
539 stream.next_in = stream.next_out = nullptr;
540 stream.avail_in = stream.avail_out = 0;
541 stream.total_in = stream.total_out = 0;
543 bool success = false;
546 int rc = deflateEnd(&stream);
547 // If we're here because of an exception, it's okay if some data
549 CHECK(rc == Z_OK || (!success && rc == Z_DATA_ERROR))
550 << rc << ": " << stream.msg;
553 uint64_t uncompressedLength = data->computeChainDataLength();
554 uint64_t maxCompressedLength = deflateBound(&stream, uncompressedLength);
556 // Max 64MiB in one go
557 constexpr uint32_t maxSingleStepLength = uint32_t(64) << 20; // 64MiB
558 constexpr uint32_t defaultBufferLength = uint32_t(4) << 20; // 4MiB
560 auto out = addOutputBuffer(
562 (maxCompressedLength <= maxSingleStepLength ?
563 maxCompressedLength :
564 defaultBufferLength));
566 for (auto& range : *data) {
567 uint64_t remaining = range.size();
568 uint64_t written = 0;
570 uint32_t step = (remaining > maxSingleStepLength ?
571 maxSingleStepLength : remaining);
572 stream.next_in = const_cast<uint8_t*>(range.data() + written);
573 stream.avail_in = step;
577 while (stream.avail_in != 0) {
578 if (stream.avail_out == 0) {
579 out->prependChain(addOutputBuffer(&stream, defaultBufferLength));
582 rc = deflate(&stream, Z_NO_FLUSH);
584 CHECK_EQ(rc, Z_OK) << stream.msg;
590 if (stream.avail_out == 0) {
591 out->prependChain(addOutputBuffer(&stream, defaultBufferLength));
594 rc = deflate(&stream, Z_FINISH);
595 } while (rc == Z_OK);
597 CHECK_EQ(rc, Z_STREAM_END) << stream.msg;
599 out->prev()->trimEnd(stream.avail_out);
601 success = true; // we survived
606 std::unique_ptr<IOBuf> ZlibCodec::doUncompress(const IOBuf* data,
607 uint64_t uncompressedLength) {
609 stream.zalloc = nullptr;
610 stream.zfree = nullptr;
611 stream.opaque = nullptr;
613 int rc = inflateInit(&stream);
615 throw std::runtime_error(to<std::string>(
616 "ZlibCodec: inflateInit error: ", rc, ": ", stream.msg));
619 stream.next_in = stream.next_out = nullptr;
620 stream.avail_in = stream.avail_out = 0;
621 stream.total_in = stream.total_out = 0;
623 bool success = false;
626 int rc = inflateEnd(&stream);
627 // If we're here because of an exception, it's okay if some data
629 CHECK(rc == Z_OK || (!success && rc == Z_DATA_ERROR))
630 << rc << ": " << stream.msg;
633 // Max 64MiB in one go
634 constexpr uint32_t maxSingleStepLength = uint32_t(64) << 20; // 64MiB
635 constexpr uint32_t defaultBufferLength = uint32_t(4) << 20; // 4MiB
637 auto out = addOutputBuffer(
639 ((uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
640 uncompressedLength <= maxSingleStepLength) ?
642 defaultBufferLength));
644 bool streamEnd = false;
645 for (auto& range : *data) {
650 stream.next_in = const_cast<uint8_t*>(range.data());
651 stream.avail_in = range.size();
653 while (stream.avail_in != 0) {
655 throw std::runtime_error(to<std::string>(
656 "ZlibCodec: junk after end of data"));
659 streamEnd = doInflate(&stream, out.get(), defaultBufferLength);
664 streamEnd = doInflate(&stream, out.get(), defaultBufferLength);
667 out->prev()->trimEnd(stream.avail_out);
669 if (uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
670 uncompressedLength != stream.total_out) {
671 throw std::runtime_error(to<std::string>(
672 "ZlibCodec: invalid uncompressed length"));
675 success = true; // we survived
680 #endif // FOLLY_HAVE_LIBZ
682 #if FOLLY_HAVE_LIBLZMA
687 class LZMA2Codec FOLLY_FINAL : public Codec {
689 static std::unique_ptr<Codec> create(int level, CodecType type);
690 explicit LZMA2Codec(int level, CodecType type);
693 bool doNeedsUncompressedLength() const FOLLY_OVERRIDE;
694 uint64_t doMaxUncompressedLength() const FOLLY_OVERRIDE;
696 bool encodeSize() const { return type() == CodecType::LZMA2_VARINT_SIZE; }
698 std::unique_ptr<IOBuf> doCompress(const IOBuf* data) FOLLY_OVERRIDE;
699 std::unique_ptr<IOBuf> doUncompress(
701 uint64_t uncompressedLength) FOLLY_OVERRIDE;
703 std::unique_ptr<IOBuf> addOutputBuffer(lzma_stream* stream, size_t length);
704 bool doInflate(lzma_stream* stream, IOBuf* head, size_t bufferLength);
709 std::unique_ptr<Codec> LZMA2Codec::create(int level, CodecType type) {
710 return make_unique<LZMA2Codec>(level, type);
713 LZMA2Codec::LZMA2Codec(int level, CodecType type) : Codec(type) {
714 DCHECK(type == CodecType::LZMA2 || type == CodecType::LZMA2_VARINT_SIZE);
716 case COMPRESSION_LEVEL_FASTEST:
719 case COMPRESSION_LEVEL_DEFAULT:
720 level = LZMA_PRESET_DEFAULT;
722 case COMPRESSION_LEVEL_BEST:
726 if (level < 0 || level > 9) {
727 throw std::invalid_argument(to<std::string>(
728 "LZMA2Codec: invalid level: ", level));
733 bool LZMA2Codec::doNeedsUncompressedLength() const {
734 return !encodeSize();
737 uint64_t LZMA2Codec::doMaxUncompressedLength() const {
738 // From lzma/base.h: "Stream is roughly 8 EiB (2^63 bytes)"
739 return uint64_t(1) << 63;
742 std::unique_ptr<IOBuf> LZMA2Codec::addOutputBuffer(
746 CHECK_EQ(stream->avail_out, 0);
748 auto buf = IOBuf::create(length);
751 stream->next_out = buf->writableData();
752 stream->avail_out = buf->length();
757 std::unique_ptr<IOBuf> LZMA2Codec::doCompress(const IOBuf* data) {
759 lzma_stream stream = LZMA_STREAM_INIT;
761 rc = lzma_easy_encoder(&stream, level_, LZMA_CHECK_NONE);
763 throw std::runtime_error(folly::to<std::string>(
764 "LZMA2Codec: lzma_easy_encoder error: ", rc));
767 SCOPE_EXIT { lzma_end(&stream); };
769 uint64_t uncompressedLength = data->computeChainDataLength();
770 uint64_t maxCompressedLength = lzma_stream_buffer_bound(uncompressedLength);
772 // Max 64MiB in one go
773 constexpr uint32_t maxSingleStepLength = uint32_t(64) << 20; // 64MiB
774 constexpr uint32_t defaultBufferLength = uint32_t(4) << 20; // 4MiB
776 auto out = addOutputBuffer(
778 (maxCompressedLength <= maxSingleStepLength ?
779 maxCompressedLength :
780 defaultBufferLength));
783 auto size = IOBuf::createCombined(kMaxVarintLength64);
784 encodeVarintToIOBuf(uncompressedLength, size.get());
785 size->appendChain(std::move(out));
786 out = std::move(size);
789 for (auto& range : *data) {
794 stream.next_in = const_cast<uint8_t*>(range.data());
795 stream.avail_in = range.size();
797 while (stream.avail_in != 0) {
798 if (stream.avail_out == 0) {
799 out->prependChain(addOutputBuffer(&stream, defaultBufferLength));
802 rc = lzma_code(&stream, LZMA_RUN);
805 throw std::runtime_error(folly::to<std::string>(
806 "LZMA2Codec: lzma_code error: ", rc));
812 if (stream.avail_out == 0) {
813 out->prependChain(addOutputBuffer(&stream, defaultBufferLength));
816 rc = lzma_code(&stream, LZMA_FINISH);
817 } while (rc == LZMA_OK);
819 if (rc != LZMA_STREAM_END) {
820 throw std::runtime_error(folly::to<std::string>(
821 "LZMA2Codec: lzma_code ended with error: ", rc));
824 out->prev()->trimEnd(stream.avail_out);
829 bool LZMA2Codec::doInflate(lzma_stream* stream,
831 size_t bufferLength) {
832 if (stream->avail_out == 0) {
833 head->prependChain(addOutputBuffer(stream, bufferLength));
836 lzma_ret rc = lzma_code(stream, LZMA_RUN);
841 case LZMA_STREAM_END:
844 throw std::runtime_error(to<std::string>(
845 "LZMA2Codec: lzma_code error: ", rc));
851 std::unique_ptr<IOBuf> LZMA2Codec::doUncompress(const IOBuf* data,
852 uint64_t uncompressedLength) {
854 lzma_stream stream = LZMA_STREAM_INIT;
856 rc = lzma_auto_decoder(&stream, std::numeric_limits<uint64_t>::max(), 0);
858 throw std::runtime_error(folly::to<std::string>(
859 "LZMA2Codec: lzma_auto_decoder error: ", rc));
862 SCOPE_EXIT { lzma_end(&stream); };
864 // Max 64MiB in one go
865 constexpr uint32_t maxSingleStepLength = uint32_t(64) << 20; // 64MiB
866 constexpr uint32_t defaultBufferLength = uint32_t(4) << 20; // 4MiB
868 folly::io::Cursor cursor(data);
869 uint64_t actualUncompressedLength;
871 actualUncompressedLength = decodeVarintFromCursor(cursor);
872 if (uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
873 uncompressedLength != actualUncompressedLength) {
874 throw std::runtime_error("LZMA2Codec: invalid uncompressed length");
877 actualUncompressedLength = uncompressedLength;
878 DCHECK_NE(actualUncompressedLength, UNKNOWN_UNCOMPRESSED_LENGTH);
881 auto out = addOutputBuffer(
883 (actualUncompressedLength <= maxSingleStepLength ?
884 actualUncompressedLength :
885 defaultBufferLength));
887 bool streamEnd = false;
888 auto buf = cursor.peek();
889 while (buf.second != 0) {
890 stream.next_in = const_cast<uint8_t*>(buf.first);
891 stream.avail_in = buf.second;
893 while (stream.avail_in != 0) {
895 throw std::runtime_error(to<std::string>(
896 "LZMA2Codec: junk after end of data"));
899 streamEnd = doInflate(&stream, out.get(), defaultBufferLength);
902 cursor.skip(buf.second);
907 streamEnd = doInflate(&stream, out.get(), defaultBufferLength);
910 out->prev()->trimEnd(stream.avail_out);
912 if (actualUncompressedLength != stream.total_out) {
913 throw std::runtime_error(to<std::string>(
914 "LZMA2Codec: invalid uncompressed length"));
920 #endif // FOLLY_HAVE_LIBLZMA
924 std::unique_ptr<Codec> getCodec(CodecType type, int level) {
925 typedef std::unique_ptr<Codec> (*CodecFactory)(int, CodecType);
927 static CodecFactory codecFactories[
928 static_cast<size_t>(CodecType::NUM_CODEC_TYPES)] = {
929 nullptr, // USER_DEFINED
930 NoCompressionCodec::create,
932 #if FOLLY_HAVE_LIBLZ4
938 #if FOLLY_HAVE_LIBSNAPPY
950 #if FOLLY_HAVE_LIBLZ4
956 #if FOLLY_HAVE_LIBLZMA
965 size_t idx = static_cast<size_t>(type);
966 if (idx >= static_cast<size_t>(CodecType::NUM_CODEC_TYPES)) {
967 throw std::invalid_argument(to<std::string>(
968 "Compression type ", idx, " not supported"));
970 auto factory = codecFactories[idx];
972 throw std::invalid_argument(to<std::string>(
973 "Compression type ", idx, " not supported"));
975 auto codec = (*factory)(level, type);
976 DCHECK_EQ(static_cast<size_t>(codec->type()), idx);