2016-02-09 23:12:00 +00:00
|
|
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
2017-07-15 23:03:42 +00:00
|
|
|
// This source code is licensed under both the GPLv2 (found in the
|
|
|
|
// COPYING file in the root directory) and Apache 2.0 License
|
|
|
|
// (found in the LICENSE.Apache file in the root directory).
|
2015-01-09 20:57:11 +00:00
|
|
|
//
|
|
|
|
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
|
|
|
//
|
|
|
|
#pragma once
|
|
|
|
|
2015-01-15 00:24:24 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <limits>
|
2015-08-27 22:40:42 +00:00
|
|
|
#include <string>
|
2015-01-15 00:24:24 +00:00
|
|
|
|
2015-01-09 20:57:11 +00:00
|
|
|
#include "rocksdb/options.h"
|
2015-01-15 00:24:24 +00:00
|
|
|
#include "util/coding.h"
|
2015-01-09 20:57:11 +00:00
|
|
|
|
|
|
|
#ifdef SNAPPY
|
|
|
|
#include <snappy.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ZLIB
|
|
|
|
#include <zlib.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef BZIP2
|
|
|
|
#include <bzlib.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(LZ4)
|
|
|
|
#include <lz4.h>
|
|
|
|
#include <lz4hc.h>
|
|
|
|
#endif
|
|
|
|
|
2015-08-27 22:40:42 +00:00
|
|
|
#if defined(ZSTD)
|
|
|
|
#include <zstd.h>
|
2017-11-03 05:46:13 +00:00
|
|
|
#if ZSTD_VERSION_NUMBER >= 800 // v0.8.0+
|
|
|
|
#include <zdict.h>
|
|
|
|
#endif // ZSTD_VERSION_NUMBER >= 800
|
|
|
|
#endif // ZSTD
|
2015-08-27 22:40:42 +00:00
|
|
|
|
2016-04-20 05:54:24 +00:00
|
|
|
#if defined(XPRESS)
|
|
|
|
#include "port/xpress.h"
|
|
|
|
#endif
|
|
|
|
|
2015-01-09 20:57:11 +00:00
|
|
|
namespace rocksdb {
|
|
|
|
|
2015-04-06 19:50:44 +00:00
|
|
|
inline bool Snappy_Supported() {
|
|
|
|
#ifdef SNAPPY
|
|
|
|
return true;
|
2017-10-19 17:48:47 +00:00
|
|
|
#else
|
2015-04-06 19:50:44 +00:00
|
|
|
return false;
|
2017-10-19 17:48:47 +00:00
|
|
|
#endif
|
2015-04-06 19:50:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool Zlib_Supported() {
|
|
|
|
#ifdef ZLIB
|
|
|
|
return true;
|
2018-01-31 20:04:52 +00:00
|
|
|
#else
|
2015-04-06 19:50:44 +00:00
|
|
|
return false;
|
2018-01-31 20:04:52 +00:00
|
|
|
#endif
|
2015-04-06 19:50:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool BZip2_Supported() {
|
|
|
|
#ifdef BZIP2
|
|
|
|
return true;
|
2017-10-19 17:48:47 +00:00
|
|
|
#else
|
2015-04-06 19:50:44 +00:00
|
|
|
return false;
|
2017-10-19 17:48:47 +00:00
|
|
|
#endif
|
2015-04-06 19:50:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool LZ4_Supported() {
|
|
|
|
#ifdef LZ4
|
|
|
|
return true;
|
2017-10-19 17:48:47 +00:00
|
|
|
#else
|
2015-04-06 19:50:44 +00:00
|
|
|
return false;
|
2017-10-19 17:48:47 +00:00
|
|
|
#endif
|
2015-04-06 19:50:44 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 05:54:24 +00:00
|
|
|
inline bool XPRESS_Supported() {
|
|
|
|
#ifdef XPRESS
|
|
|
|
return true;
|
2017-10-19 17:48:47 +00:00
|
|
|
#else
|
2016-04-20 05:54:24 +00:00
|
|
|
return false;
|
2017-10-19 17:48:47 +00:00
|
|
|
#endif
|
2016-04-20 05:54:24 +00:00
|
|
|
}
|
|
|
|
|
2015-08-27 22:40:42 +00:00
|
|
|
inline bool ZSTD_Supported() {
|
2016-09-01 22:28:40 +00:00
|
|
|
#ifdef ZSTD
|
|
|
|
// ZSTD format is finalized since version 0.8.0.
|
|
|
|
return (ZSTD_versionNumber() >= 800);
|
2017-10-19 17:48:47 +00:00
|
|
|
#else
|
2016-09-01 22:28:40 +00:00
|
|
|
return false;
|
2017-10-19 17:48:47 +00:00
|
|
|
#endif
|
2016-09-01 22:28:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool ZSTDNotFinal_Supported() {
|
2015-08-27 22:40:42 +00:00
|
|
|
#ifdef ZSTD
|
|
|
|
return true;
|
2017-10-19 17:48:47 +00:00
|
|
|
#else
|
2015-08-27 22:40:42 +00:00
|
|
|
return false;
|
2017-10-19 17:48:47 +00:00
|
|
|
#endif
|
2015-08-27 22:40:42 +00:00
|
|
|
}
|
|
|
|
|
2015-06-18 21:55:05 +00:00
|
|
|
inline bool CompressionTypeSupported(CompressionType compression_type) {
|
|
|
|
switch (compression_type) {
|
|
|
|
case kNoCompression:
|
|
|
|
return true;
|
|
|
|
case kSnappyCompression:
|
|
|
|
return Snappy_Supported();
|
|
|
|
case kZlibCompression:
|
|
|
|
return Zlib_Supported();
|
|
|
|
case kBZip2Compression:
|
|
|
|
return BZip2_Supported();
|
|
|
|
case kLZ4Compression:
|
|
|
|
return LZ4_Supported();
|
|
|
|
case kLZ4HCCompression:
|
|
|
|
return LZ4_Supported();
|
2016-04-20 05:54:24 +00:00
|
|
|
case kXpressCompression:
|
|
|
|
return XPRESS_Supported();
|
2015-08-27 22:40:42 +00:00
|
|
|
case kZSTDNotFinalCompression:
|
2016-09-01 22:28:40 +00:00
|
|
|
return ZSTDNotFinal_Supported();
|
|
|
|
case kZSTD:
|
2015-09-09 23:36:19 +00:00
|
|
|
return ZSTD_Supported();
|
2015-06-18 21:55:05 +00:00
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline std::string CompressionTypeToString(CompressionType compression_type) {
|
|
|
|
switch (compression_type) {
|
|
|
|
case kNoCompression:
|
|
|
|
return "NoCompression";
|
|
|
|
case kSnappyCompression:
|
|
|
|
return "Snappy";
|
|
|
|
case kZlibCompression:
|
|
|
|
return "Zlib";
|
|
|
|
case kBZip2Compression:
|
|
|
|
return "BZip2";
|
|
|
|
case kLZ4Compression:
|
|
|
|
return "LZ4";
|
|
|
|
case kLZ4HCCompression:
|
|
|
|
return "LZ4HC";
|
2016-04-20 05:54:24 +00:00
|
|
|
case kXpressCompression:
|
|
|
|
return "Xpress";
|
2016-09-01 22:28:40 +00:00
|
|
|
case kZSTD:
|
2015-08-27 22:40:42 +00:00
|
|
|
return "ZSTD";
|
2017-11-16 03:30:35 +00:00
|
|
|
case kZSTDNotFinalCompression:
|
|
|
|
return "ZSTDNotFinal";
|
2015-06-18 21:55:05 +00:00
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-15 00:24:24 +00:00
|
|
|
// compress_format_version can have two values:
|
|
|
|
// 1 -- decompressed sizes for BZip2 and Zlib are not included in the compressed
|
|
|
|
// block. Also, decompressed sizes for LZ4 are encoded in platform-dependent
|
|
|
|
// way.
|
|
|
|
// 2 -- Zlib, BZip2 and LZ4 encode decompressed size as Varint32 just before the
|
|
|
|
// start of compressed block. Snappy format is the same as version 1.
|
|
|
|
|
2018-02-22 20:36:41 +00:00
|
|
|
inline bool Snappy_Compress(const CompressionOptions& opts, const char* input,
|
|
|
|
size_t length, ::std::string* output) {
|
2015-01-09 20:57:11 +00:00
|
|
|
#ifdef SNAPPY
|
|
|
|
output->resize(snappy::MaxCompressedLength(length));
|
|
|
|
size_t outlen;
|
|
|
|
snappy::RawCompress(input, length, &(*output)[0], &outlen);
|
|
|
|
output->resize(outlen);
|
|
|
|
return true;
|
2017-10-19 17:48:47 +00:00
|
|
|
#else
|
2015-01-09 20:57:11 +00:00
|
|
|
return false;
|
2017-10-19 17:48:47 +00:00
|
|
|
#endif
|
2015-01-09 20:57:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool Snappy_GetUncompressedLength(const char* input, size_t length,
|
|
|
|
size_t* result) {
|
|
|
|
#ifdef SNAPPY
|
|
|
|
return snappy::GetUncompressedLength(input, length, result);
|
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool Snappy_Uncompress(const char* input, size_t length,
|
|
|
|
char* output) {
|
|
|
|
#ifdef SNAPPY
|
|
|
|
return snappy::RawUncompress(input, length, output);
|
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-01-15 00:24:24 +00:00
|
|
|
namespace compression {
|
|
|
|
// returns size
|
|
|
|
inline size_t PutDecompressedSizeInfo(std::string* output, uint32_t length) {
|
|
|
|
PutVarint32(output, length);
|
|
|
|
return output->size();
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool GetDecompressedSizeInfo(const char** input_data,
|
|
|
|
size_t* input_length,
|
|
|
|
uint32_t* output_len) {
|
|
|
|
auto new_input_data =
|
|
|
|
GetVarint32Ptr(*input_data, *input_data + *input_length, output_len);
|
|
|
|
if (new_input_data == nullptr) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*input_length -= (new_input_data - *input_data);
|
|
|
|
*input_data = new_input_data;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} // namespace compression
|
|
|
|
|
|
|
|
// compress_format_version == 1 -- decompressed size is not included in the
|
|
|
|
// block header
|
|
|
|
// compress_format_version == 2 -- decompressed size is included in the block
|
|
|
|
// header in varint32 format
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
// @param compression_dict Data for presetting the compression library's
|
|
|
|
// dictionary.
|
2015-01-15 00:24:24 +00:00
|
|
|
inline bool Zlib_Compress(const CompressionOptions& opts,
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
uint32_t compress_format_version, const char* input,
|
|
|
|
size_t length, ::std::string* output,
|
|
|
|
const Slice& compression_dict = Slice()) {
|
2015-01-09 20:57:11 +00:00
|
|
|
#ifdef ZLIB
|
2015-01-15 00:24:24 +00:00
|
|
|
if (length > std::numeric_limits<uint32_t>::max()) {
|
|
|
|
// Can't compress more than 4GB
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t output_header_len = 0;
|
|
|
|
if (compress_format_version == 2) {
|
|
|
|
output_header_len = compression::PutDecompressedSizeInfo(
|
|
|
|
output, static_cast<uint32_t>(length));
|
|
|
|
}
|
|
|
|
// Resize output to be the plain data length.
|
|
|
|
// This may not be big enough if the compression actually expands data.
|
|
|
|
output->resize(output_header_len + length);
|
|
|
|
|
2015-01-09 20:57:11 +00:00
|
|
|
// The memLevel parameter specifies how much memory should be allocated for
|
|
|
|
// the internal compression state.
|
|
|
|
// memLevel=1 uses minimum memory but is slow and reduces compression ratio.
|
|
|
|
// memLevel=9 uses maximum memory for optimal speed.
|
|
|
|
// The default value is 8. See zconf.h for more details.
|
|
|
|
static const int memLevel = 8;
|
|
|
|
z_stream _stream;
|
|
|
|
memset(&_stream, 0, sizeof(z_stream));
|
|
|
|
int st = deflateInit2(&_stream, opts.level, Z_DEFLATED, opts.window_bits,
|
|
|
|
memLevel, opts.strategy);
|
|
|
|
if (st != Z_OK) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
if (compression_dict.size()) {
|
|
|
|
// Initialize the compression library's dictionary
|
|
|
|
st = deflateSetDictionary(
|
|
|
|
&_stream, reinterpret_cast<const Bytef*>(compression_dict.data()),
|
|
|
|
static_cast<unsigned int>(compression_dict.size()));
|
|
|
|
if (st != Z_OK) {
|
2016-07-08 17:52:25 +00:00
|
|
|
deflateEnd(&_stream);
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-09 20:57:11 +00:00
|
|
|
// Compress the input, and put compressed data in output.
|
|
|
|
_stream.next_in = (Bytef *)input;
|
|
|
|
_stream.avail_in = static_cast<unsigned int>(length);
|
|
|
|
|
|
|
|
// Initialize the output size.
|
|
|
|
_stream.avail_out = static_cast<unsigned int>(length);
|
2015-01-15 00:24:24 +00:00
|
|
|
_stream.next_out = reinterpret_cast<Bytef*>(&(*output)[output_header_len]);
|
2015-01-09 20:57:11 +00:00
|
|
|
|
2016-07-08 17:52:25 +00:00
|
|
|
bool compressed = false;
|
|
|
|
st = deflate(&_stream, Z_FINISH);
|
|
|
|
if (st == Z_STREAM_END) {
|
|
|
|
compressed = true;
|
|
|
|
output->resize(output->size() - _stream.avail_out);
|
2015-01-09 20:57:11 +00:00
|
|
|
}
|
2016-07-08 17:52:25 +00:00
|
|
|
// The only return value we really care about is Z_STREAM_END.
|
|
|
|
// Z_OK means insufficient output space. This means the compression is
|
|
|
|
// bigger than decompressed size. Just fail the compression in that case.
|
2015-01-09 20:57:11 +00:00
|
|
|
|
|
|
|
deflateEnd(&_stream);
|
2016-07-08 17:52:25 +00:00
|
|
|
return compressed;
|
2018-01-31 20:04:52 +00:00
|
|
|
#else
|
2015-01-09 20:57:11 +00:00
|
|
|
return false;
|
2018-01-31 20:04:52 +00:00
|
|
|
#endif
|
2015-01-09 20:57:11 +00:00
|
|
|
}
|
|
|
|
|
2015-01-15 00:24:24 +00:00
|
|
|
// compress_format_version == 1 -- decompressed size is not included in the
|
|
|
|
// block header
|
|
|
|
// compress_format_version == 2 -- decompressed size is included in the block
|
|
|
|
// header in varint32 format
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
// @param compression_dict Data for presetting the compression library's
|
|
|
|
// dictionary.
|
2015-01-09 20:57:11 +00:00
|
|
|
inline char* Zlib_Uncompress(const char* input_data, size_t input_length,
|
2015-01-15 00:24:24 +00:00
|
|
|
int* decompress_size,
|
|
|
|
uint32_t compress_format_version,
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
const Slice& compression_dict = Slice(),
|
2015-01-15 00:24:24 +00:00
|
|
|
int windowBits = -14) {
|
2015-01-09 20:57:11 +00:00
|
|
|
#ifdef ZLIB
|
2015-01-15 00:24:24 +00:00
|
|
|
uint32_t output_len = 0;
|
|
|
|
if (compress_format_version == 2) {
|
|
|
|
if (!compression::GetDecompressedSizeInfo(&input_data, &input_length,
|
|
|
|
&output_len)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Assume the decompressed data size will 5x of compressed size, but round
|
|
|
|
// to the page size
|
|
|
|
size_t proposed_output_len = ((input_length * 5) & (~(4096 - 1))) + 4096;
|
|
|
|
output_len = static_cast<uint32_t>(
|
|
|
|
std::min(proposed_output_len,
|
|
|
|
static_cast<size_t>(std::numeric_limits<uint32_t>::max())));
|
|
|
|
}
|
|
|
|
|
2015-01-09 20:57:11 +00:00
|
|
|
z_stream _stream;
|
|
|
|
memset(&_stream, 0, sizeof(z_stream));
|
|
|
|
|
|
|
|
// For raw inflate, the windowBits should be -8..-15.
|
|
|
|
// If windowBits is bigger than zero, it will use either zlib
|
|
|
|
// header or gzip header. Adding 32 to it will do automatic detection.
|
|
|
|
int st = inflateInit2(&_stream,
|
|
|
|
windowBits > 0 ? windowBits + 32 : windowBits);
|
|
|
|
if (st != Z_OK) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
if (compression_dict.size()) {
|
|
|
|
// Initialize the compression library's dictionary
|
|
|
|
st = inflateSetDictionary(
|
|
|
|
&_stream, reinterpret_cast<const Bytef*>(compression_dict.data()),
|
|
|
|
static_cast<unsigned int>(compression_dict.size()));
|
|
|
|
if (st != Z_OK) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-09 20:57:11 +00:00
|
|
|
_stream.next_in = (Bytef *)input_data;
|
|
|
|
_stream.avail_in = static_cast<unsigned int>(input_length);
|
|
|
|
|
|
|
|
char* output = new char[output_len];
|
|
|
|
|
|
|
|
_stream.next_out = (Bytef *)output;
|
|
|
|
_stream.avail_out = static_cast<unsigned int>(output_len);
|
|
|
|
|
|
|
|
bool done = false;
|
|
|
|
while (!done) {
|
|
|
|
st = inflate(&_stream, Z_SYNC_FLUSH);
|
|
|
|
switch (st) {
|
|
|
|
case Z_STREAM_END:
|
|
|
|
done = true;
|
|
|
|
break;
|
2015-01-15 00:24:24 +00:00
|
|
|
case Z_OK: {
|
2015-01-09 20:57:11 +00:00
|
|
|
// No output space. Increase the output space by 20%.
|
2015-01-15 00:24:24 +00:00
|
|
|
// We should never run out of output space if
|
|
|
|
// compress_format_version == 2
|
|
|
|
assert(compress_format_version != 2);
|
|
|
|
size_t old_sz = output_len;
|
2015-12-09 22:06:23 +00:00
|
|
|
uint32_t output_len_delta = output_len/5;
|
2015-01-09 20:57:11 +00:00
|
|
|
output_len += output_len_delta < 10 ? 10 : output_len_delta;
|
2015-01-15 00:24:24 +00:00
|
|
|
char* tmp = new char[output_len];
|
2015-01-09 20:57:11 +00:00
|
|
|
memcpy(tmp, output, old_sz);
|
|
|
|
delete[] output;
|
|
|
|
output = tmp;
|
|
|
|
|
|
|
|
// Set more output.
|
|
|
|
_stream.next_out = (Bytef *)(output + old_sz);
|
|
|
|
_stream.avail_out = static_cast<unsigned int>(output_len - old_sz);
|
|
|
|
break;
|
2015-01-15 00:24:24 +00:00
|
|
|
}
|
2015-01-09 20:57:11 +00:00
|
|
|
case Z_BUF_ERROR:
|
|
|
|
default:
|
|
|
|
delete[] output;
|
|
|
|
inflateEnd(&_stream);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-15 00:24:24 +00:00
|
|
|
// If we encoded decompressed block size, we should have no bytes left
|
|
|
|
assert(compress_format_version != 2 || _stream.avail_out == 0);
|
2015-01-09 20:57:11 +00:00
|
|
|
*decompress_size = static_cast<int>(output_len - _stream.avail_out);
|
|
|
|
inflateEnd(&_stream);
|
|
|
|
return output;
|
2018-01-31 20:04:52 +00:00
|
|
|
#else
|
2015-01-09 20:57:11 +00:00
|
|
|
return nullptr;
|
2018-01-31 20:04:52 +00:00
|
|
|
#endif
|
2015-01-09 20:57:11 +00:00
|
|
|
}
|
|
|
|
|
2015-01-15 00:24:24 +00:00
|
|
|
// compress_format_version == 1 -- decompressed size is not included in the
|
|
|
|
// block header
|
|
|
|
// compress_format_version == 2 -- decompressed size is included in the block
|
|
|
|
// header in varint32 format
|
2018-02-22 20:36:41 +00:00
|
|
|
inline bool BZip2_Compress(const CompressionOptions& opts,
|
|
|
|
uint32_t compress_format_version,
|
|
|
|
const char* input, size_t length,
|
|
|
|
::std::string* output) {
|
2015-01-09 20:57:11 +00:00
|
|
|
#ifdef BZIP2
|
2015-01-15 00:24:24 +00:00
|
|
|
if (length > std::numeric_limits<uint32_t>::max()) {
|
|
|
|
// Can't compress more than 4GB
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
size_t output_header_len = 0;
|
|
|
|
if (compress_format_version == 2) {
|
|
|
|
output_header_len = compression::PutDecompressedSizeInfo(
|
|
|
|
output, static_cast<uint32_t>(length));
|
|
|
|
}
|
|
|
|
// Resize output to be the plain data length.
|
|
|
|
// This may not be big enough if the compression actually expands data.
|
|
|
|
output->resize(output_header_len + length);
|
|
|
|
|
|
|
|
|
2015-01-09 20:57:11 +00:00
|
|
|
bz_stream _stream;
|
|
|
|
memset(&_stream, 0, sizeof(bz_stream));
|
|
|
|
|
|
|
|
// Block size 1 is 100K.
|
|
|
|
// 0 is for silent.
|
|
|
|
// 30 is the default workFactor
|
|
|
|
int st = BZ2_bzCompressInit(&_stream, 1, 0, 30);
|
|
|
|
if (st != BZ_OK) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compress the input, and put compressed data in output.
|
|
|
|
_stream.next_in = (char *)input;
|
|
|
|
_stream.avail_in = static_cast<unsigned int>(length);
|
|
|
|
|
|
|
|
// Initialize the output size.
|
|
|
|
_stream.avail_out = static_cast<unsigned int>(length);
|
2015-01-15 00:24:24 +00:00
|
|
|
_stream.next_out = reinterpret_cast<char*>(&(*output)[output_header_len]);
|
2015-01-09 20:57:11 +00:00
|
|
|
|
2016-07-08 17:52:25 +00:00
|
|
|
bool compressed = false;
|
|
|
|
st = BZ2_bzCompress(&_stream, BZ_FINISH);
|
|
|
|
if (st == BZ_STREAM_END) {
|
|
|
|
compressed = true;
|
|
|
|
output->resize(output->size() - _stream.avail_out);
|
2015-01-09 20:57:11 +00:00
|
|
|
}
|
2016-07-08 17:52:25 +00:00
|
|
|
// The only return value we really care about is BZ_STREAM_END.
|
|
|
|
// BZ_FINISH_OK means insufficient output space. This means the compression
|
|
|
|
// is bigger than decompressed size. Just fail the compression in that case.
|
2015-01-09 20:57:11 +00:00
|
|
|
|
|
|
|
BZ2_bzCompressEnd(&_stream);
|
2016-07-08 17:52:25 +00:00
|
|
|
return compressed;
|
2018-01-31 20:04:52 +00:00
|
|
|
#else
|
2015-01-09 20:57:11 +00:00
|
|
|
return false;
|
2018-01-31 20:04:52 +00:00
|
|
|
#endif
|
2015-01-09 20:57:11 +00:00
|
|
|
}
|
|
|
|
|
2015-01-15 00:24:24 +00:00
|
|
|
// compress_format_version == 1 -- decompressed size is not included in the
|
|
|
|
// block header
|
|
|
|
// compress_format_version == 2 -- decompressed size is included in the block
|
|
|
|
// header in varint32 format
|
2015-01-09 20:57:11 +00:00
|
|
|
inline char* BZip2_Uncompress(const char* input_data, size_t input_length,
|
2015-01-15 00:24:24 +00:00
|
|
|
int* decompress_size,
|
|
|
|
uint32_t compress_format_version) {
|
2015-01-09 20:57:11 +00:00
|
|
|
#ifdef BZIP2
|
2015-01-15 00:24:24 +00:00
|
|
|
uint32_t output_len = 0;
|
|
|
|
if (compress_format_version == 2) {
|
|
|
|
if (!compression::GetDecompressedSizeInfo(&input_data, &input_length,
|
|
|
|
&output_len)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Assume the decompressed data size will 5x of compressed size, but round
|
|
|
|
// to the next page size
|
|
|
|
size_t proposed_output_len = ((input_length * 5) & (~(4096 - 1))) + 4096;
|
|
|
|
output_len = static_cast<uint32_t>(
|
|
|
|
std::min(proposed_output_len,
|
|
|
|
static_cast<size_t>(std::numeric_limits<uint32_t>::max())));
|
|
|
|
}
|
|
|
|
|
2015-01-09 20:57:11 +00:00
|
|
|
bz_stream _stream;
|
|
|
|
memset(&_stream, 0, sizeof(bz_stream));
|
|
|
|
|
|
|
|
int st = BZ2_bzDecompressInit(&_stream, 0, 0);
|
|
|
|
if (st != BZ_OK) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
_stream.next_in = (char *)input_data;
|
|
|
|
_stream.avail_in = static_cast<unsigned int>(input_length);
|
|
|
|
|
|
|
|
char* output = new char[output_len];
|
|
|
|
|
|
|
|
_stream.next_out = (char *)output;
|
|
|
|
_stream.avail_out = static_cast<unsigned int>(output_len);
|
|
|
|
|
2015-01-15 00:24:24 +00:00
|
|
|
bool done = false;
|
|
|
|
while (!done) {
|
2015-01-09 20:57:11 +00:00
|
|
|
st = BZ2_bzDecompress(&_stream);
|
|
|
|
switch (st) {
|
|
|
|
case BZ_STREAM_END:
|
2015-01-15 00:24:24 +00:00
|
|
|
done = true;
|
2015-01-09 20:57:11 +00:00
|
|
|
break;
|
2015-01-15 00:24:24 +00:00
|
|
|
case BZ_OK: {
|
2015-01-09 20:57:11 +00:00
|
|
|
// No output space. Increase the output space by 20%.
|
2015-01-15 00:24:24 +00:00
|
|
|
// We should never run out of output space if
|
|
|
|
// compress_format_version == 2
|
|
|
|
assert(compress_format_version != 2);
|
|
|
|
uint32_t old_sz = output_len;
|
|
|
|
output_len = output_len * 1.2;
|
|
|
|
char* tmp = new char[output_len];
|
2015-01-09 20:57:11 +00:00
|
|
|
memcpy(tmp, output, old_sz);
|
|
|
|
delete[] output;
|
|
|
|
output = tmp;
|
|
|
|
|
|
|
|
// Set more output.
|
|
|
|
_stream.next_out = (char *)(output + old_sz);
|
|
|
|
_stream.avail_out = static_cast<unsigned int>(output_len - old_sz);
|
|
|
|
break;
|
2015-01-15 00:24:24 +00:00
|
|
|
}
|
2015-01-09 20:57:11 +00:00
|
|
|
default:
|
|
|
|
delete[] output;
|
|
|
|
BZ2_bzDecompressEnd(&_stream);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-15 00:24:24 +00:00
|
|
|
// If we encoded decompressed block size, we should have no bytes left
|
|
|
|
assert(compress_format_version != 2 || _stream.avail_out == 0);
|
2015-01-09 20:57:11 +00:00
|
|
|
*decompress_size = static_cast<int>(output_len - _stream.avail_out);
|
|
|
|
BZ2_bzDecompressEnd(&_stream);
|
|
|
|
return output;
|
2018-01-31 20:04:52 +00:00
|
|
|
#else
|
2015-01-09 20:57:11 +00:00
|
|
|
return nullptr;
|
2018-01-31 20:04:52 +00:00
|
|
|
#endif
|
2015-01-09 20:57:11 +00:00
|
|
|
}
|
|
|
|
|
2015-01-15 00:24:24 +00:00
|
|
|
// compress_format_version == 1 -- decompressed size is included in the
|
|
|
|
// block header using memcpy, which makes database non-portable)
|
|
|
|
// compress_format_version == 2 -- decompressed size is included in the block
|
|
|
|
// header in varint32 format
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
// @param compression_dict Data for presetting the compression library's
|
|
|
|
// dictionary.
|
2018-02-22 20:36:41 +00:00
|
|
|
inline bool LZ4_Compress(const CompressionOptions& opts,
|
2015-01-15 00:24:24 +00:00
|
|
|
uint32_t compress_format_version, const char* input,
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
size_t length, ::std::string* output,
|
|
|
|
const Slice compression_dict = Slice()) {
|
2015-01-09 20:57:11 +00:00
|
|
|
#ifdef LZ4
|
2015-01-15 00:24:24 +00:00
|
|
|
if (length > std::numeric_limits<uint32_t>::max()) {
|
|
|
|
// Can't compress more than 4GB
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t output_header_len = 0;
|
|
|
|
if (compress_format_version == 2) {
|
|
|
|
// new encoding, using varint32 to store size information
|
|
|
|
output_header_len = compression::PutDecompressedSizeInfo(
|
|
|
|
output, static_cast<uint32_t>(length));
|
|
|
|
} else {
|
|
|
|
// legacy encoding, which is not really portable (depends on big/little
|
|
|
|
// endianness)
|
|
|
|
output_header_len = 8;
|
|
|
|
output->resize(output_header_len);
|
|
|
|
char* p = const_cast<char*>(output->c_str());
|
|
|
|
memcpy(p, &length, sizeof(length));
|
|
|
|
}
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
int compress_bound = LZ4_compressBound(static_cast<int>(length));
|
|
|
|
output->resize(static_cast<size_t>(output_header_len + compress_bound));
|
|
|
|
|
|
|
|
int outlen;
|
|
|
|
#if LZ4_VERSION_NUMBER >= 10400 // r124+
|
|
|
|
LZ4_stream_t* stream = LZ4_createStream();
|
|
|
|
if (compression_dict.size()) {
|
|
|
|
LZ4_loadDict(stream, compression_dict.data(),
|
|
|
|
static_cast<int>(compression_dict.size()));
|
|
|
|
}
|
2016-11-21 20:17:06 +00:00
|
|
|
#if LZ4_VERSION_NUMBER >= 10700 // r129+
|
|
|
|
outlen = LZ4_compress_fast_continue(
|
|
|
|
stream, input, &(*output)[output_header_len], static_cast<int>(length),
|
|
|
|
compress_bound, 1);
|
|
|
|
#else // up to r128
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
outlen = LZ4_compress_limitedOutput_continue(
|
|
|
|
stream, input, &(*output)[output_header_len], static_cast<int>(length),
|
|
|
|
compress_bound);
|
2016-11-21 20:17:06 +00:00
|
|
|
#endif
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
LZ4_freeStream(stream);
|
|
|
|
#else // up to r123
|
|
|
|
outlen = LZ4_compress_limitedOutput(input, &(*output)[output_header_len],
|
|
|
|
static_cast<int>(length), compress_bound);
|
|
|
|
#endif // LZ4_VERSION_NUMBER >= 10400
|
2015-01-15 00:24:24 +00:00
|
|
|
|
2015-01-09 20:57:11 +00:00
|
|
|
if (outlen == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-01-15 00:24:24 +00:00
|
|
|
output->resize(static_cast<size_t>(output_header_len + outlen));
|
2015-01-09 20:57:11 +00:00
|
|
|
return true;
|
2018-01-31 20:04:52 +00:00
|
|
|
#else // LZ4
|
2015-01-09 20:57:11 +00:00
|
|
|
return false;
|
2018-01-31 20:04:52 +00:00
|
|
|
#endif
|
2015-01-09 20:57:11 +00:00
|
|
|
}
|
|
|
|
|
2015-01-15 00:24:24 +00:00
|
|
|
// compress_format_version == 1 -- decompressed size is included in the
|
|
|
|
// block header using memcpy, which makes database non-portable)
|
|
|
|
// compress_format_version == 2 -- decompressed size is included in the block
|
|
|
|
// header in varint32 format
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
// @param compression_dict Data for presetting the compression library's
|
|
|
|
// dictionary.
|
2015-01-09 20:57:11 +00:00
|
|
|
inline char* LZ4_Uncompress(const char* input_data, size_t input_length,
|
2015-01-15 00:24:24 +00:00
|
|
|
int* decompress_size,
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
uint32_t compress_format_version,
|
|
|
|
const Slice& compression_dict = Slice()) {
|
2015-01-09 20:57:11 +00:00
|
|
|
#ifdef LZ4
|
2015-01-15 00:24:24 +00:00
|
|
|
uint32_t output_len = 0;
|
|
|
|
if (compress_format_version == 2) {
|
|
|
|
// new encoding, using varint32 to store size information
|
|
|
|
if (!compression::GetDecompressedSizeInfo(&input_data, &input_length,
|
|
|
|
&output_len)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// legacy encoding, which is not really portable (depends on big/little
|
|
|
|
// endianness)
|
|
|
|
if (input_length < 8) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
memcpy(&output_len, input_data, sizeof(output_len));
|
|
|
|
input_length -= 8;
|
|
|
|
input_data += 8;
|
2015-01-09 20:57:11 +00:00
|
|
|
}
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
|
2015-01-15 00:24:24 +00:00
|
|
|
char* output = new char[output_len];
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
#if LZ4_VERSION_NUMBER >= 10400 // r124+
|
|
|
|
LZ4_streamDecode_t* stream = LZ4_createStreamDecode();
|
|
|
|
if (compression_dict.size()) {
|
|
|
|
LZ4_setStreamDecode(stream, compression_dict.data(),
|
|
|
|
static_cast<int>(compression_dict.size()));
|
|
|
|
}
|
|
|
|
*decompress_size = LZ4_decompress_safe_continue(
|
|
|
|
stream, input_data, output, static_cast<int>(input_length),
|
|
|
|
static_cast<int>(output_len));
|
|
|
|
LZ4_freeStreamDecode(stream);
|
|
|
|
#else // up to r123
|
2015-01-15 00:24:24 +00:00
|
|
|
*decompress_size =
|
|
|
|
LZ4_decompress_safe(input_data, output, static_cast<int>(input_length),
|
|
|
|
static_cast<int>(output_len));
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
#endif // LZ4_VERSION_NUMBER >= 10400
|
|
|
|
|
2015-01-09 20:57:11 +00:00
|
|
|
if (*decompress_size < 0) {
|
|
|
|
delete[] output;
|
|
|
|
return nullptr;
|
|
|
|
}
|
2015-01-15 00:24:24 +00:00
|
|
|
assert(*decompress_size == static_cast<int>(output_len));
|
2015-01-09 20:57:11 +00:00
|
|
|
return output;
|
2018-01-31 20:04:52 +00:00
|
|
|
#else // LZ4
|
2015-01-09 20:57:11 +00:00
|
|
|
return nullptr;
|
2018-01-31 20:04:52 +00:00
|
|
|
#endif
|
2015-01-09 20:57:11 +00:00
|
|
|
}
|
|
|
|
|
2015-01-15 00:24:24 +00:00
|
|
|
// compress_format_version == 1 -- decompressed size is included in the
|
|
|
|
// block header using memcpy, which makes database non-portable)
|
|
|
|
// compress_format_version == 2 -- decompressed size is included in the block
|
|
|
|
// header in varint32 format
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
// @param compression_dict Data for presetting the compression library's
|
|
|
|
// dictionary.
|
2015-01-15 00:24:24 +00:00
|
|
|
inline bool LZ4HC_Compress(const CompressionOptions& opts,
|
|
|
|
uint32_t compress_format_version, const char* input,
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
size_t length, ::std::string* output,
|
|
|
|
const Slice& compression_dict = Slice()) {
|
2015-01-09 20:57:11 +00:00
|
|
|
#ifdef LZ4
|
2015-01-15 00:24:24 +00:00
|
|
|
if (length > std::numeric_limits<uint32_t>::max()) {
|
|
|
|
// Can't compress more than 4GB
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t output_header_len = 0;
|
|
|
|
if (compress_format_version == 2) {
|
|
|
|
// new encoding, using varint32 to store size information
|
|
|
|
output_header_len = compression::PutDecompressedSizeInfo(
|
|
|
|
output, static_cast<uint32_t>(length));
|
|
|
|
} else {
|
|
|
|
// legacy encoding, which is not really portable (depends on big/little
|
|
|
|
// endianness)
|
|
|
|
output_header_len = 8;
|
|
|
|
output->resize(output_header_len);
|
|
|
|
char* p = const_cast<char*>(output->c_str());
|
|
|
|
memcpy(p, &length, sizeof(length));
|
|
|
|
}
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
int compress_bound = LZ4_compressBound(static_cast<int>(length));
|
|
|
|
output->resize(static_cast<size_t>(output_header_len + compress_bound));
|
2015-01-15 00:24:24 +00:00
|
|
|
|
2015-01-09 20:57:11 +00:00
|
|
|
int outlen;
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
#if LZ4_VERSION_NUMBER >= 10400 // r124+
|
|
|
|
LZ4_streamHC_t* stream = LZ4_createStreamHC();
|
|
|
|
LZ4_resetStreamHC(stream, opts.level);
|
|
|
|
const char* compression_dict_data =
|
|
|
|
compression_dict.size() > 0 ? compression_dict.data() : nullptr;
|
|
|
|
size_t compression_dict_size = compression_dict.size();
|
|
|
|
LZ4_loadDictHC(stream, compression_dict_data,
|
|
|
|
static_cast<int>(compression_dict_size));
|
|
|
|
|
|
|
|
#if LZ4_VERSION_NUMBER >= 10700 // r129+
|
|
|
|
outlen =
|
|
|
|
LZ4_compress_HC_continue(stream, input, &(*output)[output_header_len],
|
|
|
|
static_cast<int>(length), compress_bound);
|
|
|
|
#else // r124-r128
|
|
|
|
outlen = LZ4_compressHC_limitedOutput_continue(
|
|
|
|
stream, input, &(*output)[output_header_len], static_cast<int>(length),
|
|
|
|
compress_bound);
|
|
|
|
#endif // LZ4_VERSION_NUMBER >= 10700
|
|
|
|
LZ4_freeStreamHC(stream);
|
|
|
|
|
|
|
|
#elif LZ4_VERSION_MAJOR // r113-r123
|
2015-01-15 00:24:24 +00:00
|
|
|
outlen = LZ4_compressHC2_limitedOutput(input, &(*output)[output_header_len],
|
|
|
|
static_cast<int>(length),
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
compress_bound, opts.level);
|
|
|
|
#else // up to r112
|
2015-01-15 00:24:24 +00:00
|
|
|
outlen =
|
|
|
|
LZ4_compressHC_limitedOutput(input, &(*output)[output_header_len],
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
static_cast<int>(length), compress_bound);
|
|
|
|
#endif // LZ4_VERSION_NUMBER >= 10400
|
|
|
|
|
2015-01-09 20:57:11 +00:00
|
|
|
if (outlen == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-01-15 00:24:24 +00:00
|
|
|
output->resize(static_cast<size_t>(output_header_len + outlen));
|
2015-01-09 20:57:11 +00:00
|
|
|
return true;
|
2018-01-31 20:04:52 +00:00
|
|
|
#else // LZ4
|
2015-01-09 20:57:11 +00:00
|
|
|
return false;
|
2018-01-31 20:04:52 +00:00
|
|
|
#endif
|
2015-01-09 20:57:11 +00:00
|
|
|
}
|
2015-01-15 00:24:24 +00:00
|
|
|
|
2018-02-22 20:36:41 +00:00
|
|
|
inline bool XPRESS_Compress(const char* input, size_t length, std::string* output) {
|
2016-04-20 05:54:24 +00:00
|
|
|
#ifdef XPRESS
|
|
|
|
return port::xpress::Compress(input, length, output);
|
2017-10-19 17:48:47 +00:00
|
|
|
#else
|
2016-04-20 05:54:24 +00:00
|
|
|
return false;
|
2017-10-19 17:48:47 +00:00
|
|
|
#endif
|
2016-04-20 05:54:24 +00:00
|
|
|
}
|
|
|
|
|
2018-02-22 20:36:41 +00:00
|
|
|
inline char* XPRESS_Uncompress(const char* input_data, size_t input_length,
|
|
|
|
int* decompress_size) {
|
2016-04-20 05:54:24 +00:00
|
|
|
#ifdef XPRESS
|
|
|
|
return port::xpress::Decompress(input_data, input_length, decompress_size);
|
2017-10-19 17:48:47 +00:00
|
|
|
#else
|
2016-04-20 05:54:24 +00:00
|
|
|
return nullptr;
|
2017-10-19 17:48:47 +00:00
|
|
|
#endif
|
2016-04-20 05:54:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
// @param compression_dict Data for presetting the compression library's
|
|
|
|
// dictionary.
|
2015-08-27 22:40:42 +00:00
|
|
|
inline bool ZSTD_Compress(const CompressionOptions& opts, const char* input,
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
size_t length, ::std::string* output,
|
|
|
|
const Slice& compression_dict = Slice()) {
|
2015-08-27 22:40:42 +00:00
|
|
|
#ifdef ZSTD
|
|
|
|
if (length > std::numeric_limits<uint32_t>::max()) {
|
|
|
|
// Can't compress more than 4GB
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t output_header_len = compression::PutDecompressedSizeInfo(
|
|
|
|
output, static_cast<uint32_t>(length));
|
|
|
|
|
|
|
|
size_t compressBound = ZSTD_compressBound(length);
|
|
|
|
output->resize(static_cast<size_t>(output_header_len + compressBound));
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
size_t outlen;
|
|
|
|
#if ZSTD_VERSION_NUMBER >= 500 // v0.5.0+
|
|
|
|
ZSTD_CCtx* context = ZSTD_createCCtx();
|
|
|
|
outlen = ZSTD_compress_usingDict(
|
|
|
|
context, &(*output)[output_header_len], compressBound, input, length,
|
|
|
|
compression_dict.data(), compression_dict.size(), opts.level);
|
|
|
|
ZSTD_freeCCtx(context);
|
|
|
|
#else // up to v0.4.x
|
|
|
|
outlen = ZSTD_compress(&(*output)[output_header_len], compressBound, input,
|
|
|
|
length, opts.level);
|
|
|
|
#endif // ZSTD_VERSION_NUMBER >= 500
|
2015-08-27 22:40:42 +00:00
|
|
|
if (outlen == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
output->resize(output_header_len + outlen);
|
|
|
|
return true;
|
2018-01-31 20:04:52 +00:00
|
|
|
#else // ZSTD
|
2015-08-27 22:40:42 +00:00
|
|
|
return false;
|
2018-01-31 20:04:52 +00:00
|
|
|
#endif
|
2015-08-27 22:40:42 +00:00
|
|
|
}
|
|
|
|
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
// @param compression_dict Data for presetting the compression library's
|
|
|
|
// dictionary.
|
2015-08-27 22:40:42 +00:00
|
|
|
inline char* ZSTD_Uncompress(const char* input_data, size_t input_length,
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
int* decompress_size,
|
|
|
|
const Slice& compression_dict = Slice()) {
|
2015-08-27 22:40:42 +00:00
|
|
|
#ifdef ZSTD
|
|
|
|
uint32_t output_len = 0;
|
|
|
|
if (!compression::GetDecompressedSizeInfo(&input_data, &input_length,
|
|
|
|
&output_len)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
char* output = new char[output_len];
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
size_t actual_output_length;
|
|
|
|
#if ZSTD_VERSION_NUMBER >= 500 // v0.5.0+
|
|
|
|
ZSTD_DCtx* context = ZSTD_createDCtx();
|
|
|
|
actual_output_length = ZSTD_decompress_usingDict(
|
|
|
|
context, output, output_len, input_data, input_length,
|
|
|
|
compression_dict.data(), compression_dict.size());
|
|
|
|
ZSTD_freeDCtx(context);
|
|
|
|
#else // up to v0.4.x
|
|
|
|
actual_output_length =
|
2015-08-27 22:40:42 +00:00
|
|
|
ZSTD_decompress(output, output_len, input_data, input_length);
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 00:36:03 +00:00
|
|
|
#endif // ZSTD_VERSION_NUMBER >= 500
|
2015-08-27 22:40:42 +00:00
|
|
|
assert(actual_output_length == output_len);
|
|
|
|
*decompress_size = static_cast<int>(actual_output_length);
|
|
|
|
return output;
|
2018-01-31 20:04:52 +00:00
|
|
|
#else // ZSTD
|
2015-08-27 22:40:42 +00:00
|
|
|
return nullptr;
|
2018-01-31 20:04:52 +00:00
|
|
|
#endif
|
2015-08-27 22:40:42 +00:00
|
|
|
}
|
|
|
|
|
2017-11-03 05:46:13 +00:00
|
|
|
inline std::string ZSTD_TrainDictionary(const std::string& samples,
|
|
|
|
const std::vector<size_t>& sample_lens,
|
|
|
|
size_t max_dict_bytes) {
|
|
|
|
// Dictionary trainer is available since v0.6.1, but ZSTD was marked stable
|
|
|
|
// only since v0.8.0. For now we enable the feature in stable versions only.
|
|
|
|
#if ZSTD_VERSION_NUMBER >= 800 // v0.8.0+
|
|
|
|
std::string dict_data(max_dict_bytes, '\0');
|
2017-11-03 18:15:26 +00:00
|
|
|
size_t dict_len = ZDICT_trainFromBuffer(
|
|
|
|
&dict_data[0], max_dict_bytes, &samples[0], &sample_lens[0],
|
|
|
|
static_cast<unsigned>(sample_lens.size()));
|
2017-11-03 05:46:13 +00:00
|
|
|
if (ZDICT_isError(dict_len)) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
assert(dict_len <= max_dict_bytes);
|
|
|
|
dict_data.resize(dict_len);
|
|
|
|
return dict_data;
|
|
|
|
#else // up to v0.7.x
|
|
|
|
assert(false);
|
|
|
|
return "";
|
|
|
|
#endif // ZSTD_VERSION_NUMBER >= 800
|
|
|
|
}
|
|
|
|
|
|
|
|
inline std::string ZSTD_TrainDictionary(const std::string& samples,
|
|
|
|
size_t sample_len_shift,
|
|
|
|
size_t max_dict_bytes) {
|
|
|
|
// Dictionary trainer is available since v0.6.1, but ZSTD was marked stable
|
|
|
|
// only since v0.8.0. For now we enable the feature in stable versions only.
|
|
|
|
#if ZSTD_VERSION_NUMBER >= 800 // v0.8.0+
|
|
|
|
// skips potential partial sample at the end of "samples"
|
|
|
|
size_t num_samples = samples.size() >> sample_len_shift;
|
|
|
|
std::vector<size_t> sample_lens(num_samples, 1 << sample_len_shift);
|
|
|
|
return ZSTD_TrainDictionary(samples, sample_lens, max_dict_bytes);
|
|
|
|
#else // up to v0.7.x
|
|
|
|
assert(false);
|
|
|
|
return "";
|
|
|
|
#endif // ZSTD_VERSION_NUMBER >= 800
|
|
|
|
}
|
|
|
|
|
2015-01-15 00:24:24 +00:00
|
|
|
} // namespace rocksdb
|