2 * Copyright 2013 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"
19 // Yes, tr1, as that's what gtest requires
23 #include <unordered_map>
25 #include <glog/logging.h>
26 #include <gtest/gtest.h>
28 #include "folly/Benchmark.h"
29 #include "folly/Hash.h"
30 #include "folly/Random.h"
31 #include "folly/io/IOBufQueue.h"
33 namespace folly { namespace io { namespace test {
35 constexpr size_t randomDataSizeLog2 = 27; // 128MiB
36 constexpr size_t randomDataSize = size_t(1) << randomDataSizeLog2;
38 std::unique_ptr<uint8_t[]> randomData;
39 std::unordered_map<uint64_t, uint64_t> hashes;
41 uint64_t hashIOBuf(const IOBuf* buf) {
42 uint64_t h = folly::hash::FNV_64_HASH_START;
43 for (auto& range : *buf) {
44 h = folly::hash::fnv64_buf(range.data(), range.size(), h);
49 uint64_t getRandomDataHash(uint64_t size) {
50 auto p = hashes.find(size);
51 if (p != hashes.end()) {
55 uint64_t h = folly::hash::fnv64_buf(randomData.get(), size);
60 void generateRandomData() {
61 randomData.reset(new uint8_t[size_t(1) << randomDataSizeLog2]);
63 constexpr size_t numThreadsLog2 = 3;
64 constexpr size_t numThreads = size_t(1) << numThreadsLog2;
66 uint32_t seed = randomNumberSeed();
68 std::vector<std::thread> threads;
69 threads.reserve(numThreads);
70 for (size_t t = 0; t < numThreads; ++t) {
72 [seed, t, numThreadsLog2] () {
73 std::mt19937 rng(seed + t);
74 size_t countLog2 = size_t(1) << (randomDataSizeLog2 - numThreadsLog2);
75 size_t start = size_t(t) << countLog2;
76 for (size_t i = 0; i < countLog2; ++i) {
77 randomData[start + i] = rng();
82 for (auto& t : threads) {
87 class CompressionTest : public testing::TestWithParam<
88 std::tr1::tuple<int, CodecType>> {
91 auto tup = GetParam();
92 uncompressedLength_ = uint64_t(1) << std::tr1::get<0>(tup);
93 codec_ = getCodec(std::tr1::get<1>(tup));
96 uint64_t uncompressedLength_;
97 std::unique_ptr<Codec> codec_;
100 TEST_P(CompressionTest, Simple) {
101 auto original = IOBuf::wrapBuffer(randomData.get(), uncompressedLength_);
102 auto compressed = codec_->compress(original.get());
103 if (!codec_->needsUncompressedLength()) {
104 auto uncompressed = codec_->uncompress(compressed.get());
105 EXPECT_EQ(uncompressedLength_, uncompressed->computeChainDataLength());
106 EXPECT_EQ(getRandomDataHash(uncompressedLength_),
107 hashIOBuf(uncompressed.get()));
110 auto uncompressed = codec_->uncompress(compressed.get(),
111 uncompressedLength_);
112 EXPECT_EQ(uncompressedLength_, uncompressed->computeChainDataLength());
113 EXPECT_EQ(getRandomDataHash(uncompressedLength_),
114 hashIOBuf(uncompressed.get()));
118 INSTANTIATE_TEST_CASE_P(
122 testing::Values(0, 1, 12, 22, int(randomDataSizeLog2)),
123 testing::Values(CodecType::NO_COMPRESSION,
128 class CompressionCorruptionTest : public testing::TestWithParam<CodecType> {
131 codec_ = getCodec(GetParam());
134 std::unique_ptr<Codec> codec_;
137 TEST_P(CompressionCorruptionTest, Simple) {
138 constexpr uint64_t uncompressedLength = 42;
139 auto original = IOBuf::wrapBuffer(randomData.get(), uncompressedLength);
140 auto compressed = codec_->compress(original.get());
142 if (!codec_->needsUncompressedLength()) {
143 auto uncompressed = codec_->uncompress(compressed.get());
144 EXPECT_EQ(uncompressedLength, uncompressed->computeChainDataLength());
145 EXPECT_EQ(getRandomDataHash(uncompressedLength),
146 hashIOBuf(uncompressed.get()));
149 auto uncompressed = codec_->uncompress(compressed.get(),
151 EXPECT_EQ(uncompressedLength, uncompressed->computeChainDataLength());
152 EXPECT_EQ(getRandomDataHash(uncompressedLength),
153 hashIOBuf(uncompressed.get()));
156 EXPECT_THROW(codec_->uncompress(compressed.get(), uncompressedLength + 1),
159 // Corrupt the first character
160 ++(compressed->writableData()[0]);
162 if (!codec_->needsUncompressedLength()) {
163 EXPECT_THROW(codec_->uncompress(compressed.get()),
167 EXPECT_THROW(codec_->uncompress(compressed.get(), uncompressedLength),
171 INSTANTIATE_TEST_CASE_P(
172 CompressionCorruptionTest,
173 CompressionCorruptionTest,
175 // NO_COMPRESSION can't detect corruption
176 // LZ4 can't detect corruption reliably (sigh)
182 int main(int argc, char *argv[]) {
183 testing::InitGoogleTest(&argc, argv);
184 google::ParseCommandLineFlags(&argc, &argv, true);
186 folly::io::test::generateRandomData(); // 4GB
188 auto ret = RUN_ALL_TESTS();
190 folly::runBenchmarksOnFlag();