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"
21 #include <glog/logging.h>
23 #include <snappy-sinksource.h>
27 #include "folly/Conv.h"
28 #include "folly/Memory.h"
29 #include "folly/Portability.h"
30 #include "folly/ScopeGuard.h"
31 #include "folly/Varint.h"
32 #include "folly/io/Cursor.h"
34 namespace folly { namespace io {
36 Codec::Codec(CodecType type) : type_(type) { }
38 // Ensure consistent behavior in the nullptr case
39 std::unique_ptr<IOBuf> Codec::compress(const IOBuf* data) {
40 return !data->empty() ? doCompress(data) : IOBuf::create(0);
43 std::unique_ptr<IOBuf> Codec::uncompress(const IOBuf* data,
44 uint64_t uncompressedLength) {
45 if (uncompressedLength == UNKNOWN_UNCOMPRESSED_LENGTH) {
46 if (needsUncompressedLength()) {
47 throw std::invalid_argument("Codec: uncompressed length required");
49 } else if (uncompressedLength > maxUncompressedLength()) {
50 throw std::runtime_error("Codec: uncompressed length too large");
54 if (uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
55 uncompressedLength != 0) {
56 throw std::runtime_error("Codec: invalid uncompressed length");
58 return IOBuf::create(0);
61 return doUncompress(data, uncompressedLength);
64 bool Codec::needsUncompressedLength() const {
65 return doNeedsUncompressedLength();
68 uint64_t Codec::maxUncompressedLength() const {
69 return doMaxUncompressedLength();
72 bool Codec::doNeedsUncompressedLength() const {
76 uint64_t Codec::doMaxUncompressedLength() const {
77 return std::numeric_limits<uint64_t>::max() - 1;
85 class NoCompressionCodec FOLLY_FINAL : public Codec {
87 static std::unique_ptr<Codec> create(int level, CodecType type);
88 explicit NoCompressionCodec(int level, CodecType type);
91 std::unique_ptr<IOBuf> doCompress(const IOBuf* data) FOLLY_OVERRIDE;
92 std::unique_ptr<IOBuf> doUncompress(
94 uint64_t uncompressedLength) FOLLY_OVERRIDE;
97 std::unique_ptr<Codec> NoCompressionCodec::create(int level, CodecType type) {
98 return make_unique<NoCompressionCodec>(level, type);
101 NoCompressionCodec::NoCompressionCodec(int level, CodecType type)
103 DCHECK(type == CodecType::NO_COMPRESSION);
105 case COMPRESSION_LEVEL_DEFAULT:
106 case COMPRESSION_LEVEL_FASTEST:
107 case COMPRESSION_LEVEL_BEST:
111 throw std::invalid_argument(to<std::string>(
112 "NoCompressionCodec: invalid level ", level));
116 std::unique_ptr<IOBuf> NoCompressionCodec::doCompress(
118 return data->clone();
121 std::unique_ptr<IOBuf> NoCompressionCodec::doUncompress(
123 uint64_t uncompressedLength) {
124 if (uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
125 data->computeChainDataLength() != uncompressedLength) {
126 throw std::runtime_error(to<std::string>(
127 "NoCompressionCodec: invalid uncompressed length"));
129 return data->clone();
135 class LZ4Codec FOLLY_FINAL : public Codec {
137 static std::unique_ptr<Codec> create(int level, CodecType type);
138 explicit LZ4Codec(int level, CodecType type);
141 bool doNeedsUncompressedLength() const FOLLY_OVERRIDE;
142 uint64_t doMaxUncompressedLength() const FOLLY_OVERRIDE;
144 bool encodeSize() const { return type() == CodecType::LZ4_VARINT_SIZE; }
146 std::unique_ptr<IOBuf> doCompress(const IOBuf* data) FOLLY_OVERRIDE;
147 std::unique_ptr<IOBuf> doUncompress(
149 uint64_t uncompressedLength) FOLLY_OVERRIDE;
151 bool highCompression_;
154 std::unique_ptr<Codec> LZ4Codec::create(int level, CodecType type) {
155 return make_unique<LZ4Codec>(level, type);
158 LZ4Codec::LZ4Codec(int level, CodecType type) : Codec(type) {
159 DCHECK(type == CodecType::LZ4 || type == CodecType::LZ4_VARINT_SIZE);
162 case COMPRESSION_LEVEL_FASTEST:
163 case COMPRESSION_LEVEL_DEFAULT:
166 case COMPRESSION_LEVEL_BEST:
170 if (level < 1 || level > 2) {
171 throw std::invalid_argument(to<std::string>(
172 "LZ4Codec: invalid level: ", level));
174 highCompression_ = (level > 1);
177 bool LZ4Codec::doNeedsUncompressedLength() const {
178 return !encodeSize();
181 uint64_t LZ4Codec::doMaxUncompressedLength() const {
182 // From lz4.h: "Max supported value is ~1.9GB"; I wish we had something
184 return 1.8 * (uint64_t(1) << 30);
189 void encodeVarintToIOBuf(uint64_t val, folly::IOBuf* out) {
190 DCHECK_GE(out->tailroom(), kMaxVarintLength64);
191 out->append(encodeVarint(val, out->writableTail()));
194 uint64_t decodeVarintFromCursor(folly::io::Cursor& cursor) {
195 // Must have enough room in *this* buffer.
196 auto p = cursor.peek();
197 folly::ByteRange range(p.first, p.second);
198 uint64_t val = decodeVarint(range);
199 cursor.skip(range.data() - p.first);
205 std::unique_ptr<IOBuf> LZ4Codec::doCompress(const IOBuf* data) {
206 std::unique_ptr<IOBuf> clone;
207 if (data->isChained()) {
208 // LZ4 doesn't support streaming, so we have to coalesce
209 clone = data->clone();
214 uint32_t extraSize = encodeSize() ? kMaxVarintLength64 : 0;
215 auto out = IOBuf::create(extraSize + LZ4_compressBound(data->length()));
217 encodeVarintToIOBuf(data->length(), out.get());
221 if (highCompression_) {
222 n = LZ4_compressHC(reinterpret_cast<const char*>(data->data()),
223 reinterpret_cast<char*>(out->writableTail()),
226 n = LZ4_compress(reinterpret_cast<const char*>(data->data()),
227 reinterpret_cast<char*>(out->writableTail()),
232 CHECK_LE(n, out->capacity());
238 std::unique_ptr<IOBuf> LZ4Codec::doUncompress(
240 uint64_t uncompressedLength) {
241 std::unique_ptr<IOBuf> clone;
242 if (data->isChained()) {
243 // LZ4 doesn't support streaming, so we have to coalesce
244 clone = data->clone();
249 folly::io::Cursor cursor(data);
250 uint64_t actualUncompressedLength;
252 actualUncompressedLength = decodeVarintFromCursor(cursor);
253 if (uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
254 uncompressedLength != actualUncompressedLength) {
255 throw std::runtime_error("LZ4Codec: invalid uncompressed length");
258 actualUncompressedLength = uncompressedLength;
259 DCHECK_NE(actualUncompressedLength, UNKNOWN_UNCOMPRESSED_LENGTH);
262 auto out = IOBuf::create(actualUncompressedLength);
263 auto p = cursor.peek();
264 int n = LZ4_uncompress(reinterpret_cast<const char*>(p.first),
265 reinterpret_cast<char*>(out->writableTail()),
266 actualUncompressedLength);
268 throw std::runtime_error(to<std::string>(
269 "LZ4 decompression returned invalid value ", n));
271 out->append(actualUncompressedLength);
280 * Implementation of snappy::Source that reads from a IOBuf chain.
282 class IOBufSnappySource FOLLY_FINAL : public snappy::Source {
284 explicit IOBufSnappySource(const IOBuf* data);
285 size_t Available() const FOLLY_OVERRIDE;
286 const char* Peek(size_t* len) FOLLY_OVERRIDE;
287 void Skip(size_t n) FOLLY_OVERRIDE;
293 IOBufSnappySource::IOBufSnappySource(const IOBuf* data)
294 : available_(data->computeChainDataLength()),
298 size_t IOBufSnappySource::Available() const {
302 const char* IOBufSnappySource::Peek(size_t* len) {
303 auto p = cursor_.peek();
305 return reinterpret_cast<const char*>(p.first);
308 void IOBufSnappySource::Skip(size_t n) {
309 CHECK_LE(n, available_);
314 class SnappyCodec FOLLY_FINAL : public Codec {
316 static std::unique_ptr<Codec> create(int level, CodecType type);
317 explicit SnappyCodec(int level, CodecType type);
320 uint64_t doMaxUncompressedLength() const FOLLY_OVERRIDE;
321 std::unique_ptr<IOBuf> doCompress(const IOBuf* data) FOLLY_OVERRIDE;
322 std::unique_ptr<IOBuf> doUncompress(
324 uint64_t uncompressedLength) FOLLY_OVERRIDE;
327 std::unique_ptr<Codec> SnappyCodec::create(int level, CodecType type) {
328 return make_unique<SnappyCodec>(level, type);
331 SnappyCodec::SnappyCodec(int level, CodecType type) : Codec(type) {
332 DCHECK(type == CodecType::SNAPPY);
334 case COMPRESSION_LEVEL_FASTEST:
335 case COMPRESSION_LEVEL_DEFAULT:
336 case COMPRESSION_LEVEL_BEST:
340 throw std::invalid_argument(to<std::string>(
341 "SnappyCodec: invalid level: ", level));
345 uint64_t SnappyCodec::doMaxUncompressedLength() const {
346 // snappy.h uses uint32_t for lengths, so there's that.
347 return std::numeric_limits<uint32_t>::max();
350 std::unique_ptr<IOBuf> SnappyCodec::doCompress(const IOBuf* data) {
351 IOBufSnappySource source(data);
353 IOBuf::create(snappy::MaxCompressedLength(source.Available()));
355 snappy::UncheckedByteArraySink sink(reinterpret_cast<char*>(
356 out->writableTail()));
358 size_t n = snappy::Compress(&source, &sink);
360 CHECK_LE(n, out->capacity());
365 std::unique_ptr<IOBuf> SnappyCodec::doUncompress(const IOBuf* data,
366 uint64_t uncompressedLength) {
367 uint32_t actualUncompressedLength = 0;
370 IOBufSnappySource source(data);
371 if (!snappy::GetUncompressedLength(&source, &actualUncompressedLength)) {
372 throw std::runtime_error("snappy::GetUncompressedLength failed");
374 if (uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
375 uncompressedLength != actualUncompressedLength) {
376 throw std::runtime_error("snappy: invalid uncompressed length");
380 auto out = IOBuf::create(actualUncompressedLength);
383 IOBufSnappySource source(data);
384 if (!snappy::RawUncompress(&source,
385 reinterpret_cast<char*>(out->writableTail()))) {
386 throw std::runtime_error("snappy::RawUncompress failed");
390 out->append(actualUncompressedLength);
397 class ZlibCodec FOLLY_FINAL : public Codec {
399 static std::unique_ptr<Codec> create(int level, CodecType type);
400 explicit ZlibCodec(int level, CodecType type);
403 std::unique_ptr<IOBuf> doCompress(const IOBuf* data) FOLLY_OVERRIDE;
404 std::unique_ptr<IOBuf> doUncompress(
406 uint64_t uncompressedLength) FOLLY_OVERRIDE;
408 std::unique_ptr<IOBuf> addOutputBuffer(z_stream* stream, uint32_t length);
409 bool doInflate(z_stream* stream, IOBuf* head, uint32_t bufferLength);
414 std::unique_ptr<Codec> ZlibCodec::create(int level, CodecType type) {
415 return make_unique<ZlibCodec>(level, type);
418 ZlibCodec::ZlibCodec(int level, CodecType type) : Codec(type) {
419 DCHECK(type == CodecType::ZLIB);
421 case COMPRESSION_LEVEL_FASTEST:
424 case COMPRESSION_LEVEL_DEFAULT:
425 level = Z_DEFAULT_COMPRESSION;
427 case COMPRESSION_LEVEL_BEST:
431 if (level != Z_DEFAULT_COMPRESSION && (level < 0 || level > 9)) {
432 throw std::invalid_argument(to<std::string>(
433 "ZlibCodec: invalid level: ", level));
438 std::unique_ptr<IOBuf> ZlibCodec::addOutputBuffer(z_stream* stream,
440 CHECK_EQ(stream->avail_out, 0);
442 auto buf = IOBuf::create(length);
445 stream->next_out = buf->writableData();
446 stream->avail_out = buf->length();
451 bool ZlibCodec::doInflate(z_stream* stream,
453 uint32_t bufferLength) {
454 if (stream->avail_out == 0) {
455 head->prependChain(addOutputBuffer(stream, bufferLength));
458 int rc = inflate(stream, Z_NO_FLUSH);
469 throw std::runtime_error(to<std::string>(
470 "ZlibCodec: inflate error: ", rc, ": ", stream->msg));
472 CHECK(false) << rc << ": " << stream->msg;
479 std::unique_ptr<IOBuf> ZlibCodec::doCompress(const IOBuf* data) {
481 stream.zalloc = nullptr;
482 stream.zfree = nullptr;
483 stream.opaque = nullptr;
485 int rc = deflateInit(&stream, level_);
487 throw std::runtime_error(to<std::string>(
488 "ZlibCodec: deflateInit error: ", rc, ": ", stream.msg));
491 stream.next_in = stream.next_out = nullptr;
492 stream.avail_in = stream.avail_out = 0;
493 stream.total_in = stream.total_out = 0;
495 bool success = false;
498 int rc = deflateEnd(&stream);
499 // If we're here because of an exception, it's okay if some data
501 CHECK(rc == Z_OK || (!success && rc == Z_DATA_ERROR))
502 << rc << ": " << stream.msg;
505 uint64_t uncompressedLength = data->computeChainDataLength();
506 uint64_t maxCompressedLength = deflateBound(&stream, uncompressedLength);
508 // Max 64MiB in one go
509 constexpr uint32_t maxSingleStepLength = uint32_t(64) << 20; // 64MiB
510 constexpr uint32_t defaultBufferLength = uint32_t(4) << 20; // 4MiB
512 auto out = addOutputBuffer(
514 (maxCompressedLength <= maxSingleStepLength ?
515 maxCompressedLength :
516 defaultBufferLength));
518 for (auto& range : *data) {
523 stream.next_in = const_cast<uint8_t*>(range.data());
524 stream.avail_in = range.size();
526 while (stream.avail_in != 0) {
527 if (stream.avail_out == 0) {
528 out->prependChain(addOutputBuffer(&stream, defaultBufferLength));
531 rc = deflate(&stream, Z_NO_FLUSH);
533 CHECK_EQ(rc, Z_OK) << stream.msg;
538 if (stream.avail_out == 0) {
539 out->prependChain(addOutputBuffer(&stream, defaultBufferLength));
542 rc = deflate(&stream, Z_FINISH);
543 } while (rc == Z_OK);
545 CHECK_EQ(rc, Z_STREAM_END) << stream.msg;
547 out->prev()->trimEnd(stream.avail_out);
549 success = true; // we survived
554 std::unique_ptr<IOBuf> ZlibCodec::doUncompress(const IOBuf* data,
555 uint64_t uncompressedLength) {
557 stream.zalloc = nullptr;
558 stream.zfree = nullptr;
559 stream.opaque = nullptr;
561 int rc = inflateInit(&stream);
563 throw std::runtime_error(to<std::string>(
564 "ZlibCodec: inflateInit error: ", rc, ": ", stream.msg));
567 stream.next_in = stream.next_out = nullptr;
568 stream.avail_in = stream.avail_out = 0;
569 stream.total_in = stream.total_out = 0;
571 bool success = false;
574 int rc = inflateEnd(&stream);
575 // If we're here because of an exception, it's okay if some data
577 CHECK(rc == Z_OK || (!success && rc == Z_DATA_ERROR))
578 << rc << ": " << stream.msg;
581 // Max 64MiB in one go
582 constexpr uint32_t maxSingleStepLength = uint32_t(64) << 20; // 64MiB
583 constexpr uint32_t defaultBufferLength = uint32_t(4) << 20; // 4MiB
585 auto out = addOutputBuffer(
587 ((uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
588 uncompressedLength <= maxSingleStepLength) ?
590 defaultBufferLength));
592 bool streamEnd = false;
593 for (auto& range : *data) {
598 stream.next_in = const_cast<uint8_t*>(range.data());
599 stream.avail_in = range.size();
601 while (stream.avail_in != 0) {
603 throw std::runtime_error(to<std::string>(
604 "ZlibCodec: junk after end of data"));
607 streamEnd = doInflate(&stream, out.get(), defaultBufferLength);
612 streamEnd = doInflate(&stream, out.get(), defaultBufferLength);
615 out->prev()->trimEnd(stream.avail_out);
617 if (uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
618 uncompressedLength != stream.total_out) {
619 throw std::runtime_error(to<std::string>(
620 "ZlibCodec: invalid uncompressed length"));
623 success = true; // we survived
631 class LZMA2Codec FOLLY_FINAL : public Codec {
633 static std::unique_ptr<Codec> create(int level, CodecType type);
634 explicit LZMA2Codec(int level, CodecType type);
637 bool doNeedsUncompressedLength() const FOLLY_OVERRIDE;
638 uint64_t doMaxUncompressedLength() const FOLLY_OVERRIDE;
640 bool encodeSize() const { return type() == CodecType::LZMA2_VARINT_SIZE; }
642 std::unique_ptr<IOBuf> doCompress(const IOBuf* data) FOLLY_OVERRIDE;
643 std::unique_ptr<IOBuf> doUncompress(
645 uint64_t uncompressedLength) FOLLY_OVERRIDE;
647 std::unique_ptr<IOBuf> addOutputBuffer(lzma_stream* stream, size_t length);
648 bool doInflate(lzma_stream* stream, IOBuf* head, size_t bufferLength);
653 std::unique_ptr<Codec> LZMA2Codec::create(int level, CodecType type) {
654 return make_unique<LZMA2Codec>(level, type);
657 LZMA2Codec::LZMA2Codec(int level, CodecType type) : Codec(type) {
658 DCHECK(type == CodecType::LZMA2 || type == CodecType::LZMA2_VARINT_SIZE);
660 case COMPRESSION_LEVEL_FASTEST:
663 case COMPRESSION_LEVEL_DEFAULT:
664 level = LZMA_PRESET_DEFAULT;
666 case COMPRESSION_LEVEL_BEST:
670 if (level < 0 || level > 9) {
671 throw std::invalid_argument(to<std::string>(
672 "LZMA2Codec: invalid level: ", level));
677 bool LZMA2Codec::doNeedsUncompressedLength() const {
678 return !encodeSize();
681 uint64_t LZMA2Codec::doMaxUncompressedLength() const {
682 // From lzma/base.h: "Stream is roughly 8 EiB (2^63 bytes)"
683 return uint64_t(1) << 63;
686 std::unique_ptr<IOBuf> LZMA2Codec::addOutputBuffer(
690 CHECK_EQ(stream->avail_out, 0);
692 auto buf = IOBuf::create(length);
695 stream->next_out = buf->writableData();
696 stream->avail_out = buf->length();
701 std::unique_ptr<IOBuf> LZMA2Codec::doCompress(const IOBuf* data) {
703 lzma_stream stream = LZMA_STREAM_INIT;
705 rc = lzma_easy_encoder(&stream, level_, LZMA_CHECK_NONE);
707 throw std::runtime_error(folly::to<std::string>(
708 "LZMA2Codec: lzma_easy_encoder error: ", rc));
711 SCOPE_EXIT { lzma_end(&stream); };
713 uint64_t uncompressedLength = data->computeChainDataLength();
714 uint64_t maxCompressedLength = lzma_stream_buffer_bound(uncompressedLength);
716 // Max 64MiB in one go
717 constexpr uint32_t maxSingleStepLength = uint32_t(64) << 20; // 64MiB
718 constexpr uint32_t defaultBufferLength = uint32_t(4) << 20; // 4MiB
720 auto out = addOutputBuffer(
722 (maxCompressedLength <= maxSingleStepLength ?
723 maxCompressedLength :
724 defaultBufferLength));
727 auto size = IOBuf::createCombined(kMaxVarintLength64);
728 encodeVarintToIOBuf(uncompressedLength, size.get());
729 size->appendChain(std::move(out));
730 out = std::move(size);
733 for (auto& range : *data) {
738 stream.next_in = const_cast<uint8_t*>(range.data());
739 stream.avail_in = range.size();
741 while (stream.avail_in != 0) {
742 if (stream.avail_out == 0) {
743 out->prependChain(addOutputBuffer(&stream, defaultBufferLength));
746 rc = lzma_code(&stream, LZMA_RUN);
749 throw std::runtime_error(folly::to<std::string>(
750 "LZMA2Codec: lzma_code error: ", rc));
756 if (stream.avail_out == 0) {
757 out->prependChain(addOutputBuffer(&stream, defaultBufferLength));
760 rc = lzma_code(&stream, LZMA_FINISH);
761 } while (rc == LZMA_OK);
763 if (rc != LZMA_STREAM_END) {
764 throw std::runtime_error(folly::to<std::string>(
765 "LZMA2Codec: lzma_code ended with error: ", rc));
768 out->prev()->trimEnd(stream.avail_out);
773 bool LZMA2Codec::doInflate(lzma_stream* stream,
775 size_t bufferLength) {
776 if (stream->avail_out == 0) {
777 head->prependChain(addOutputBuffer(stream, bufferLength));
780 lzma_ret rc = lzma_code(stream, LZMA_RUN);
785 case LZMA_STREAM_END:
788 throw std::runtime_error(to<std::string>(
789 "LZMA2Codec: lzma_code error: ", rc));
795 std::unique_ptr<IOBuf> LZMA2Codec::doUncompress(const IOBuf* data,
796 uint64_t uncompressedLength) {
798 lzma_stream stream = LZMA_STREAM_INIT;
800 rc = lzma_auto_decoder(&stream, std::numeric_limits<uint64_t>::max(), 0);
802 throw std::runtime_error(folly::to<std::string>(
803 "LZMA2Codec: lzma_auto_decoder error: ", rc));
806 SCOPE_EXIT { lzma_end(&stream); };
808 // Max 64MiB in one go
809 constexpr uint32_t maxSingleStepLength = uint32_t(64) << 20; // 64MiB
810 constexpr uint32_t defaultBufferLength = uint32_t(4) << 20; // 4MiB
812 folly::io::Cursor cursor(data);
813 uint64_t actualUncompressedLength;
815 actualUncompressedLength = decodeVarintFromCursor(cursor);
816 if (uncompressedLength != UNKNOWN_UNCOMPRESSED_LENGTH &&
817 uncompressedLength != actualUncompressedLength) {
818 throw std::runtime_error("LZMA2Codec: invalid uncompressed length");
821 actualUncompressedLength = uncompressedLength;
822 DCHECK_NE(actualUncompressedLength, UNKNOWN_UNCOMPRESSED_LENGTH);
825 auto out = addOutputBuffer(
827 (actualUncompressedLength <= maxSingleStepLength ?
828 actualUncompressedLength :
829 defaultBufferLength));
831 bool streamEnd = false;
832 auto buf = cursor.peek();
833 while (buf.second != 0) {
834 stream.next_in = const_cast<uint8_t*>(buf.first);
835 stream.avail_in = buf.second;
837 while (stream.avail_in != 0) {
839 throw std::runtime_error(to<std::string>(
840 "LZMA2Codec: junk after end of data"));
843 streamEnd = doInflate(&stream, out.get(), defaultBufferLength);
846 cursor.skip(buf.second);
851 streamEnd = doInflate(&stream, out.get(), defaultBufferLength);
854 out->prev()->trimEnd(stream.avail_out);
856 if (actualUncompressedLength != stream.total_out) {
857 throw std::runtime_error(to<std::string>(
858 "LZMA2Codec: invalid uncompressed length"));
865 typedef std::unique_ptr<Codec> (*CodecFactory)(int, CodecType);
867 CodecFactory gCodecFactories[
868 static_cast<size_t>(CodecType::NUM_CODEC_TYPES)] = {
869 nullptr, // USER_DEFINED
870 NoCompressionCodec::create,
881 std::unique_ptr<Codec> getCodec(CodecType type, int level) {
882 size_t idx = static_cast<size_t>(type);
883 if (idx >= static_cast<size_t>(CodecType::NUM_CODEC_TYPES)) {
884 throw std::invalid_argument(to<std::string>(
885 "Compression type ", idx, " not supported"));
887 auto factory = gCodecFactories[idx];
889 throw std::invalid_argument(to<std::string>(
890 "Compression type ", idx, " not supported"));
892 auto codec = (*factory)(level, type);
893 DCHECK_EQ(static_cast<size_t>(codec->type()), idx);