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();
153 void encodeVarintToIOBuf(uint64_t val, folly::IOBuf* out) {
154 DCHECK_GE(out->tailroom(), kMaxVarintLength64);
155 out->append(encodeVarint(val, out->writableTail()));
158 uint64_t decodeVarintFromCursor(folly::io::Cursor& cursor) {
159 // Must have enough room in *this* buffer.
160 auto p = cursor.peek();
161 folly::ByteRange range(p.first, p.second);
162 uint64_t val = decodeVarint(range);
163 cursor.skip(range.data() - p.first);
169 #if FOLLY_HAVE_LIBLZ4
174 class LZ4Codec FOLLY_FINAL : public Codec {
176 static std::unique_ptr<Codec> create(int level, CodecType type);
177 explicit LZ4Codec(int level, CodecType type);
180 bool doNeedsUncompressedLength() const FOLLY_OVERRIDE;
181 uint64_t doMaxUncompressedLength() const FOLLY_OVERRIDE;
183 bool encodeSize() const { return type() == CodecType::LZ4_VARINT_SIZE; }
185 std::unique_ptr<IOBuf> doCompress(const IOBuf* data) FOLLY_OVERRIDE;
186 std::unique_ptr<IOBuf> doUncompress(
188 uint64_t uncompressedLength) FOLLY_OVERRIDE;
190 bool highCompression_;
193 std::unique_ptr<Codec> LZ4Codec::create(int level, CodecType type) {
194 return make_unique<LZ4Codec>(level, type);
197 LZ4Codec::LZ4Codec(int level, CodecType type) : Codec(type) {
198 DCHECK(type == CodecType::LZ4 || type == CodecType::LZ4_VARINT_SIZE);
201 case COMPRESSION_LEVEL_FASTEST:
202 case COMPRESSION_LEVEL_DEFAULT:
205 case COMPRESSION_LEVEL_BEST:
209 if (level < 1 || level > 2) {
210 throw std::invalid_argument(to<std::string>(
211 "LZ4Codec: invalid level: ", level));
213 highCompression_ = (level > 1);
216 bool LZ4Codec::doNeedsUncompressedLength() const {
217 return !encodeSize();
220 // The value comes from lz4.h in lz4-r117, but older versions of lz4 don't
221 // define LZ4_MAX_INPUT_SIZE (even though the max size is the same), so do it
223 #ifndef LZ4_MAX_INPUT_SIZE
224 # define LZ4_MAX_INPUT_SIZE 0x7E000000
227 uint64_t LZ4Codec::doMaxUncompressedLength() const {
228 return LZ4_MAX_INPUT_SIZE;
231 std::unique_ptr<IOBuf> LZ4Codec::doCompress(const IOBuf* data) {
232 std::unique_ptr<IOBuf> clone;
233 if (data->isChained()) {
234 // LZ4 doesn't support streaming, so we have to coalesce
235 clone = data->clone();
240 uint32_t extraSize = encodeSize() ? kMaxVarintLength64 : 0;
241 auto out = IOBuf::create(extraSize + LZ4_compressBound(data->length()));
243 encodeVarintToIOBuf(data->length(), out.get());
247 if (highCompression_) {
248 n = LZ4_compressHC(reinterpret_cast<const char*>(data->data()),
249 reinterpret_cast<char*>(out->writableTail()),
252 n = LZ4_compress(reinterpret_cast<const char*>(data->data()),
253 reinterpret_cast<char*>(out->writableTail()),
258 CHECK_LE(n, out->capacity());
264 std::unique_ptr<IOBuf> LZ4Codec::doUncompress(
266 uint64_t uncompressedLength) {
267 std::unique_ptr<IOBuf> clone;
268 if (data->isChained()) {
269 // LZ4 doesn't support streaming, so we have to coalesce
270 clone = data->clone();
275 folly::io::Cursor cursor(data);
276 uint64_t actualUncompressedLength;
278 actualUncompressedLength = decodeVarintFromCursor(cursor);
279 if (uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
280 uncompressedLength != actualUncompressedLength) {
281 throw std::runtime_error("LZ4Codec: invalid uncompressed length");
284 actualUncompressedLength = uncompressedLength;
285 if (actualUncompressedLength == UNKNOWN_UNCOMPRESSED_LENGTH ||
286 actualUncompressedLength > maxUncompressedLength()) {
287 throw std::runtime_error("LZ4Codec: invalid uncompressed length");
291 auto p = cursor.peek();
292 auto out = IOBuf::create(actualUncompressedLength);
293 int n = LZ4_decompress_safe(reinterpret_cast<const char*>(p.first),
294 reinterpret_cast<char*>(out->writableTail()),
296 actualUncompressedLength);
298 if (n < 0 || uint64_t(n) != actualUncompressedLength) {
299 throw std::runtime_error(to<std::string>(
300 "LZ4 decompression returned invalid value ", n));
302 out->append(actualUncompressedLength);
306 #endif // FOLLY_HAVE_LIBLZ4
308 #if FOLLY_HAVE_LIBSNAPPY
315 * Implementation of snappy::Source that reads from a IOBuf chain.
317 class IOBufSnappySource FOLLY_FINAL : public snappy::Source {
319 explicit IOBufSnappySource(const IOBuf* data);
320 size_t Available() const FOLLY_OVERRIDE;
321 const char* Peek(size_t* len) FOLLY_OVERRIDE;
322 void Skip(size_t n) FOLLY_OVERRIDE;
328 IOBufSnappySource::IOBufSnappySource(const IOBuf* data)
329 : available_(data->computeChainDataLength()),
333 size_t IOBufSnappySource::Available() const {
337 const char* IOBufSnappySource::Peek(size_t* len) {
338 auto p = cursor_.peek();
340 return reinterpret_cast<const char*>(p.first);
343 void IOBufSnappySource::Skip(size_t n) {
344 CHECK_LE(n, available_);
349 class SnappyCodec FOLLY_FINAL : public Codec {
351 static std::unique_ptr<Codec> create(int level, CodecType type);
352 explicit SnappyCodec(int level, CodecType type);
355 uint64_t doMaxUncompressedLength() const FOLLY_OVERRIDE;
356 std::unique_ptr<IOBuf> doCompress(const IOBuf* data) FOLLY_OVERRIDE;
357 std::unique_ptr<IOBuf> doUncompress(
359 uint64_t uncompressedLength) FOLLY_OVERRIDE;
362 std::unique_ptr<Codec> SnappyCodec::create(int level, CodecType type) {
363 return make_unique<SnappyCodec>(level, type);
366 SnappyCodec::SnappyCodec(int level, CodecType type) : Codec(type) {
367 DCHECK(type == CodecType::SNAPPY);
369 case COMPRESSION_LEVEL_FASTEST:
370 case COMPRESSION_LEVEL_DEFAULT:
371 case COMPRESSION_LEVEL_BEST:
375 throw std::invalid_argument(to<std::string>(
376 "SnappyCodec: invalid level: ", level));
380 uint64_t SnappyCodec::doMaxUncompressedLength() const {
381 // snappy.h uses uint32_t for lengths, so there's that.
382 return std::numeric_limits<uint32_t>::max();
385 std::unique_ptr<IOBuf> SnappyCodec::doCompress(const IOBuf* data) {
386 IOBufSnappySource source(data);
388 IOBuf::create(snappy::MaxCompressedLength(source.Available()));
390 snappy::UncheckedByteArraySink sink(reinterpret_cast<char*>(
391 out->writableTail()));
393 size_t n = snappy::Compress(&source, &sink);
395 CHECK_LE(n, out->capacity());
400 std::unique_ptr<IOBuf> SnappyCodec::doUncompress(const IOBuf* data,
401 uint64_t uncompressedLength) {
402 uint32_t actualUncompressedLength = 0;
405 IOBufSnappySource source(data);
406 if (!snappy::GetUncompressedLength(&source, &actualUncompressedLength)) {
407 throw std::runtime_error("snappy::GetUncompressedLength failed");
409 if (uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
410 uncompressedLength != actualUncompressedLength) {
411 throw std::runtime_error("snappy: invalid uncompressed length");
415 auto out = IOBuf::create(actualUncompressedLength);
418 IOBufSnappySource source(data);
419 if (!snappy::RawUncompress(&source,
420 reinterpret_cast<char*>(out->writableTail()))) {
421 throw std::runtime_error("snappy::RawUncompress failed");
425 out->append(actualUncompressedLength);
429 #endif // FOLLY_HAVE_LIBSNAPPY
435 class ZlibCodec FOLLY_FINAL : public Codec {
437 static std::unique_ptr<Codec> create(int level, CodecType type);
438 explicit ZlibCodec(int level, CodecType type);
441 std::unique_ptr<IOBuf> doCompress(const IOBuf* data) FOLLY_OVERRIDE;
442 std::unique_ptr<IOBuf> doUncompress(
444 uint64_t uncompressedLength) FOLLY_OVERRIDE;
446 std::unique_ptr<IOBuf> addOutputBuffer(z_stream* stream, uint32_t length);
447 bool doInflate(z_stream* stream, IOBuf* head, uint32_t bufferLength);
452 std::unique_ptr<Codec> ZlibCodec::create(int level, CodecType type) {
453 return make_unique<ZlibCodec>(level, type);
456 ZlibCodec::ZlibCodec(int level, CodecType type) : Codec(type) {
457 DCHECK(type == CodecType::ZLIB);
459 case COMPRESSION_LEVEL_FASTEST:
462 case COMPRESSION_LEVEL_DEFAULT:
463 level = Z_DEFAULT_COMPRESSION;
465 case COMPRESSION_LEVEL_BEST:
469 if (level != Z_DEFAULT_COMPRESSION && (level < 0 || level > 9)) {
470 throw std::invalid_argument(to<std::string>(
471 "ZlibCodec: invalid level: ", level));
476 std::unique_ptr<IOBuf> ZlibCodec::addOutputBuffer(z_stream* stream,
478 CHECK_EQ(stream->avail_out, 0);
480 auto buf = IOBuf::create(length);
483 stream->next_out = buf->writableData();
484 stream->avail_out = buf->length();
489 bool ZlibCodec::doInflate(z_stream* stream,
491 uint32_t bufferLength) {
492 if (stream->avail_out == 0) {
493 head->prependChain(addOutputBuffer(stream, bufferLength));
496 int rc = inflate(stream, Z_NO_FLUSH);
507 throw std::runtime_error(to<std::string>(
508 "ZlibCodec: inflate error: ", rc, ": ", stream->msg));
510 CHECK(false) << rc << ": " << stream->msg;
516 std::unique_ptr<IOBuf> ZlibCodec::doCompress(const IOBuf* data) {
518 stream.zalloc = nullptr;
519 stream.zfree = nullptr;
520 stream.opaque = nullptr;
522 int rc = deflateInit(&stream, level_);
524 throw std::runtime_error(to<std::string>(
525 "ZlibCodec: deflateInit error: ", rc, ": ", stream.msg));
528 stream.next_in = stream.next_out = nullptr;
529 stream.avail_in = stream.avail_out = 0;
530 stream.total_in = stream.total_out = 0;
532 bool success = false;
535 int rc = deflateEnd(&stream);
536 // If we're here because of an exception, it's okay if some data
538 CHECK(rc == Z_OK || (!success && rc == Z_DATA_ERROR))
539 << rc << ": " << stream.msg;
542 uint64_t uncompressedLength = data->computeChainDataLength();
543 uint64_t maxCompressedLength = deflateBound(&stream, uncompressedLength);
545 // Max 64MiB in one go
546 constexpr uint32_t maxSingleStepLength = uint32_t(64) << 20; // 64MiB
547 constexpr uint32_t defaultBufferLength = uint32_t(4) << 20; // 4MiB
549 auto out = addOutputBuffer(
551 (maxCompressedLength <= maxSingleStepLength ?
552 maxCompressedLength :
553 defaultBufferLength));
555 for (auto& range : *data) {
556 uint64_t remaining = range.size();
557 uint64_t written = 0;
559 uint32_t step = (remaining > maxSingleStepLength ?
560 maxSingleStepLength : remaining);
561 stream.next_in = const_cast<uint8_t*>(range.data() + written);
562 stream.avail_in = step;
566 while (stream.avail_in != 0) {
567 if (stream.avail_out == 0) {
568 out->prependChain(addOutputBuffer(&stream, defaultBufferLength));
571 rc = deflate(&stream, Z_NO_FLUSH);
573 CHECK_EQ(rc, Z_OK) << stream.msg;
579 if (stream.avail_out == 0) {
580 out->prependChain(addOutputBuffer(&stream, defaultBufferLength));
583 rc = deflate(&stream, Z_FINISH);
584 } while (rc == Z_OK);
586 CHECK_EQ(rc, Z_STREAM_END) << stream.msg;
588 out->prev()->trimEnd(stream.avail_out);
590 success = true; // we survived
595 std::unique_ptr<IOBuf> ZlibCodec::doUncompress(const IOBuf* data,
596 uint64_t uncompressedLength) {
598 stream.zalloc = nullptr;
599 stream.zfree = nullptr;
600 stream.opaque = nullptr;
602 int rc = inflateInit(&stream);
604 throw std::runtime_error(to<std::string>(
605 "ZlibCodec: inflateInit error: ", rc, ": ", stream.msg));
608 stream.next_in = stream.next_out = nullptr;
609 stream.avail_in = stream.avail_out = 0;
610 stream.total_in = stream.total_out = 0;
612 bool success = false;
615 int rc = inflateEnd(&stream);
616 // If we're here because of an exception, it's okay if some data
618 CHECK(rc == Z_OK || (!success && rc == Z_DATA_ERROR))
619 << rc << ": " << stream.msg;
622 // Max 64MiB in one go
623 constexpr uint32_t maxSingleStepLength = uint32_t(64) << 20; // 64MiB
624 constexpr uint32_t defaultBufferLength = uint32_t(4) << 20; // 4MiB
626 auto out = addOutputBuffer(
628 ((uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
629 uncompressedLength <= maxSingleStepLength) ?
631 defaultBufferLength));
633 bool streamEnd = false;
634 for (auto& range : *data) {
639 stream.next_in = const_cast<uint8_t*>(range.data());
640 stream.avail_in = range.size();
642 while (stream.avail_in != 0) {
644 throw std::runtime_error(to<std::string>(
645 "ZlibCodec: junk after end of data"));
648 streamEnd = doInflate(&stream, out.get(), defaultBufferLength);
653 streamEnd = doInflate(&stream, out.get(), defaultBufferLength);
656 out->prev()->trimEnd(stream.avail_out);
658 if (uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
659 uncompressedLength != stream.total_out) {
660 throw std::runtime_error(to<std::string>(
661 "ZlibCodec: invalid uncompressed length"));
664 success = true; // we survived
669 #endif // FOLLY_HAVE_LIBZ
671 #if FOLLY_HAVE_LIBLZMA
676 class LZMA2Codec FOLLY_FINAL : public Codec {
678 static std::unique_ptr<Codec> create(int level, CodecType type);
679 explicit LZMA2Codec(int level, CodecType type);
682 bool doNeedsUncompressedLength() const FOLLY_OVERRIDE;
683 uint64_t doMaxUncompressedLength() const FOLLY_OVERRIDE;
685 bool encodeSize() const { return type() == CodecType::LZMA2_VARINT_SIZE; }
687 std::unique_ptr<IOBuf> doCompress(const IOBuf* data) FOLLY_OVERRIDE;
688 std::unique_ptr<IOBuf> doUncompress(
690 uint64_t uncompressedLength) FOLLY_OVERRIDE;
692 std::unique_ptr<IOBuf> addOutputBuffer(lzma_stream* stream, size_t length);
693 bool doInflate(lzma_stream* stream, IOBuf* head, size_t bufferLength);
698 std::unique_ptr<Codec> LZMA2Codec::create(int level, CodecType type) {
699 return make_unique<LZMA2Codec>(level, type);
702 LZMA2Codec::LZMA2Codec(int level, CodecType type) : Codec(type) {
703 DCHECK(type == CodecType::LZMA2 || type == CodecType::LZMA2_VARINT_SIZE);
705 case COMPRESSION_LEVEL_FASTEST:
708 case COMPRESSION_LEVEL_DEFAULT:
709 level = LZMA_PRESET_DEFAULT;
711 case COMPRESSION_LEVEL_BEST:
715 if (level < 0 || level > 9) {
716 throw std::invalid_argument(to<std::string>(
717 "LZMA2Codec: invalid level: ", level));
722 bool LZMA2Codec::doNeedsUncompressedLength() const {
723 return !encodeSize();
726 uint64_t LZMA2Codec::doMaxUncompressedLength() const {
727 // From lzma/base.h: "Stream is roughly 8 EiB (2^63 bytes)"
728 return uint64_t(1) << 63;
731 std::unique_ptr<IOBuf> LZMA2Codec::addOutputBuffer(
735 CHECK_EQ(stream->avail_out, 0);
737 auto buf = IOBuf::create(length);
740 stream->next_out = buf->writableData();
741 stream->avail_out = buf->length();
746 std::unique_ptr<IOBuf> LZMA2Codec::doCompress(const IOBuf* data) {
748 lzma_stream stream = LZMA_STREAM_INIT;
750 rc = lzma_easy_encoder(&stream, level_, LZMA_CHECK_NONE);
752 throw std::runtime_error(folly::to<std::string>(
753 "LZMA2Codec: lzma_easy_encoder error: ", rc));
756 SCOPE_EXIT { lzma_end(&stream); };
758 uint64_t uncompressedLength = data->computeChainDataLength();
759 uint64_t maxCompressedLength = lzma_stream_buffer_bound(uncompressedLength);
761 // Max 64MiB in one go
762 constexpr uint32_t maxSingleStepLength = uint32_t(64) << 20; // 64MiB
763 constexpr uint32_t defaultBufferLength = uint32_t(4) << 20; // 4MiB
765 auto out = addOutputBuffer(
767 (maxCompressedLength <= maxSingleStepLength ?
768 maxCompressedLength :
769 defaultBufferLength));
772 auto size = IOBuf::createCombined(kMaxVarintLength64);
773 encodeVarintToIOBuf(uncompressedLength, size.get());
774 size->appendChain(std::move(out));
775 out = std::move(size);
778 for (auto& range : *data) {
783 stream.next_in = const_cast<uint8_t*>(range.data());
784 stream.avail_in = range.size();
786 while (stream.avail_in != 0) {
787 if (stream.avail_out == 0) {
788 out->prependChain(addOutputBuffer(&stream, defaultBufferLength));
791 rc = lzma_code(&stream, LZMA_RUN);
794 throw std::runtime_error(folly::to<std::string>(
795 "LZMA2Codec: lzma_code error: ", rc));
801 if (stream.avail_out == 0) {
802 out->prependChain(addOutputBuffer(&stream, defaultBufferLength));
805 rc = lzma_code(&stream, LZMA_FINISH);
806 } while (rc == LZMA_OK);
808 if (rc != LZMA_STREAM_END) {
809 throw std::runtime_error(folly::to<std::string>(
810 "LZMA2Codec: lzma_code ended with error: ", rc));
813 out->prev()->trimEnd(stream.avail_out);
818 bool LZMA2Codec::doInflate(lzma_stream* stream,
820 size_t bufferLength) {
821 if (stream->avail_out == 0) {
822 head->prependChain(addOutputBuffer(stream, bufferLength));
825 lzma_ret rc = lzma_code(stream, LZMA_RUN);
830 case LZMA_STREAM_END:
833 throw std::runtime_error(to<std::string>(
834 "LZMA2Codec: lzma_code error: ", rc));
840 std::unique_ptr<IOBuf> LZMA2Codec::doUncompress(const IOBuf* data,
841 uint64_t uncompressedLength) {
843 lzma_stream stream = LZMA_STREAM_INIT;
845 rc = lzma_auto_decoder(&stream, std::numeric_limits<uint64_t>::max(), 0);
847 throw std::runtime_error(folly::to<std::string>(
848 "LZMA2Codec: lzma_auto_decoder error: ", rc));
851 SCOPE_EXIT { lzma_end(&stream); };
853 // Max 64MiB in one go
854 constexpr uint32_t maxSingleStepLength = uint32_t(64) << 20; // 64MiB
855 constexpr uint32_t defaultBufferLength = uint32_t(4) << 20; // 4MiB
857 folly::io::Cursor cursor(data);
858 uint64_t actualUncompressedLength;
860 actualUncompressedLength = decodeVarintFromCursor(cursor);
861 if (uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
862 uncompressedLength != actualUncompressedLength) {
863 throw std::runtime_error("LZMA2Codec: invalid uncompressed length");
866 actualUncompressedLength = uncompressedLength;
867 DCHECK_NE(actualUncompressedLength, UNKNOWN_UNCOMPRESSED_LENGTH);
870 auto out = addOutputBuffer(
872 (actualUncompressedLength <= maxSingleStepLength ?
873 actualUncompressedLength :
874 defaultBufferLength));
876 bool streamEnd = false;
877 auto buf = cursor.peek();
878 while (buf.second != 0) {
879 stream.next_in = const_cast<uint8_t*>(buf.first);
880 stream.avail_in = buf.second;
882 while (stream.avail_in != 0) {
884 throw std::runtime_error(to<std::string>(
885 "LZMA2Codec: junk after end of data"));
888 streamEnd = doInflate(&stream, out.get(), defaultBufferLength);
891 cursor.skip(buf.second);
896 streamEnd = doInflate(&stream, out.get(), defaultBufferLength);
899 out->prev()->trimEnd(stream.avail_out);
901 if (actualUncompressedLength != stream.total_out) {
902 throw std::runtime_error(to<std::string>(
903 "LZMA2Codec: invalid uncompressed length"));
909 #endif // FOLLY_HAVE_LIBLZMA
911 typedef std::unique_ptr<Codec> (*CodecFactory)(int, CodecType);
913 CodecFactory gCodecFactories[
914 static_cast<size_t>(CodecType::NUM_CODEC_TYPES)] = {
915 nullptr, // USER_DEFINED
916 NoCompressionCodec::create,
918 #if FOLLY_HAVE_LIBLZ4
924 #if FOLLY_HAVE_LIBSNAPPY
936 #if FOLLY_HAVE_LIBLZ4
942 #if FOLLY_HAVE_LIBLZMA
953 std::unique_ptr<Codec> getCodec(CodecType type, int level) {
954 size_t idx = static_cast<size_t>(type);
955 if (idx >= static_cast<size_t>(CodecType::NUM_CODEC_TYPES)) {
956 throw std::invalid_argument(to<std::string>(
957 "Compression type ", idx, " not supported"));
959 auto factory = gCodecFactories[idx];
961 throw std::invalid_argument(to<std::string>(
962 "Compression type ", idx, " not supported"));
964 auto codec = (*factory)(level, type);
965 DCHECK_EQ(static_cast<size_t>(codec->type()), idx);