mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-31 01:43:03 +08:00
3e4d6a9b7f
Add Base94 encoding/decoding implementation and tests. Bug: crashpad:308 Change-Id: If3f25efcb277eacd5d8cbe1d66f22919872c7d64 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/1796682 Commit-Queue: Tao Bai <michaelbai@chromium.org> Reviewed-by: Joshua Peraza <jperaza@chromium.org>
291 lines
10 KiB
C++
291 lines
10 KiB
C++
// Copyright 2019 The Crashpad Authors. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "util/stream/base94_output_stream.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <algorithm>
|
|
#include <sstream>
|
|
|
|
#include "base/macros.h"
|
|
#include "base/rand_util.h"
|
|
#include "base/stl_util.h"
|
|
#include "base/strings/stringprintf.h"
|
|
#include "gtest/gtest.h"
|
|
#include "util/stream/test_output_stream.h"
|
|
|
|
namespace crashpad {
|
|
namespace test {
|
|
namespace {
|
|
|
|
constexpr size_t kLongDataLength = 4096 * 10;
|
|
|
|
std::string DumpInput(const uint8_t* input, size_t size) {
|
|
std::stringstream s;
|
|
size_t index = 0;
|
|
size_t byte_count = 0;
|
|
while (index < size) {
|
|
s << "0x" << std::hex << static_cast<int>(*(input + index++)) << ",";
|
|
if (byte_count++ > 1024) {
|
|
s << "\n";
|
|
byte_count = 0;
|
|
}
|
|
}
|
|
return s.str();
|
|
}
|
|
|
|
class Base94OutputStreamTest : public testing::Test {
|
|
public:
|
|
Base94OutputStreamTest() {}
|
|
|
|
protected:
|
|
void SetUp() override {
|
|
auto output_stream = std::make_unique<TestOutputStream>();
|
|
encode_test_output_stream_ = output_stream.get();
|
|
encoder_ = std::make_unique<Base94OutputStream>(
|
|
Base94OutputStream::Mode::kEncode, std::move(output_stream));
|
|
output_stream = std::make_unique<TestOutputStream>();
|
|
decode_test_output_stream_ = output_stream.get();
|
|
decoder_ = std::make_unique<Base94OutputStream>(
|
|
Base94OutputStream::Mode::kDecode, std::move(output_stream));
|
|
output_stream = std::make_unique<TestOutputStream>();
|
|
round_trip_test_output_stream_ = output_stream.get();
|
|
round_trip_ = std::make_unique<Base94OutputStream>(
|
|
Base94OutputStream::Mode::kEncode,
|
|
std::make_unique<Base94OutputStream>(Base94OutputStream::Mode::kDecode,
|
|
std::move(output_stream)));
|
|
}
|
|
|
|
const uint8_t* BuildDeterministicInput(size_t size) {
|
|
deterministic_input_ = std::make_unique<uint8_t[]>(size);
|
|
uint8_t* deterministic_input_base = deterministic_input_.get();
|
|
while (size-- > 0)
|
|
deterministic_input_base[size] = static_cast<uint8_t>(size);
|
|
return deterministic_input_base;
|
|
}
|
|
|
|
const uint8_t* BuildRandomInput(size_t size) {
|
|
input_ = std::make_unique<uint8_t[]>(size);
|
|
base::RandBytes(&input_[0], size);
|
|
return input_.get();
|
|
}
|
|
|
|
Base94OutputStream* round_trip() const { return round_trip_.get(); }
|
|
const TestOutputStream& round_trip_test_output_stream() const {
|
|
return *round_trip_test_output_stream_;
|
|
}
|
|
|
|
static void VerifyEncoding(const TestOutputStream& out,
|
|
const std::string& expected) {
|
|
EXPECT_EQ(out.all_data().size(), expected.size());
|
|
EXPECT_EQ(memcmp(out.all_data().data(), expected.data(), expected.size()),
|
|
0);
|
|
}
|
|
|
|
static void VerifyDecoding(const TestOutputStream& out,
|
|
const std::vector<uint8_t>& expected) {
|
|
EXPECT_EQ(out.all_data().size(), expected.size());
|
|
EXPECT_EQ(memcmp(out.all_data().data(), expected.data(), expected.size()),
|
|
0);
|
|
}
|
|
|
|
void RunTest(const std::string& text, const std::vector<uint8_t>& binary) {
|
|
EXPECT_TRUE(encoder_->Write(binary.data(), binary.size()));
|
|
EXPECT_TRUE(encoder_->Flush());
|
|
VerifyEncoding(*encode_test_output_stream_, text);
|
|
EXPECT_TRUE(decoder_->Write(reinterpret_cast<const uint8_t*>(text.data()),
|
|
text.size()));
|
|
EXPECT_TRUE(decoder_->Flush());
|
|
VerifyDecoding(*decode_test_output_stream_, binary);
|
|
}
|
|
|
|
void VerifyRoundTrip(const std::vector<uint8_t>& expected) {
|
|
TestOutputStream* out = round_trip_test_output_stream_;
|
|
EXPECT_EQ(out->all_data().size(), expected.size());
|
|
EXPECT_EQ(memcmp(out->all_data().data(), expected.data(), expected.size()),
|
|
0);
|
|
}
|
|
|
|
private:
|
|
std::unique_ptr<Base94OutputStream> encoder_;
|
|
std::unique_ptr<Base94OutputStream> decoder_;
|
|
std::unique_ptr<Base94OutputStream> round_trip_;
|
|
TestOutputStream* encode_test_output_stream_;
|
|
TestOutputStream* decode_test_output_stream_;
|
|
TestOutputStream* round_trip_test_output_stream_;
|
|
std::unique_ptr<uint8_t[]> input_;
|
|
std::unique_ptr<uint8_t[]> deterministic_input_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(Base94OutputStreamTest);
|
|
};
|
|
|
|
TEST_F(Base94OutputStreamTest, Encoding) {
|
|
std::vector<uint8_t> binary = {0x0};
|
|
std::string text("!");
|
|
RunTest(text, binary);
|
|
}
|
|
|
|
TEST_F(Base94OutputStreamTest, Encoding1) {
|
|
std::vector<uint8_t> binary = {0x0, 0x0};
|
|
std::string text("!!!");
|
|
RunTest(text, binary);
|
|
}
|
|
|
|
TEST_F(Base94OutputStreamTest, Encoding2) {
|
|
std::vector<uint8_t> binary = {0x0, 0x0, 0x0};
|
|
std::string text("!!!!");
|
|
RunTest(text, binary);
|
|
}
|
|
|
|
TEST_F(Base94OutputStreamTest, Encoding3) {
|
|
std::vector<uint8_t> binary = {0x0, 0x0, 0x0, 0x0};
|
|
std::string text("!!!!!");
|
|
RunTest(text, binary);
|
|
}
|
|
|
|
TEST_F(Base94OutputStreamTest, Encoding4) {
|
|
std::vector<uint8_t> binary = {0x0, 0x0, 0x0, 0x0, 0x0};
|
|
std::string text("!!!!!!");
|
|
RunTest(text, binary);
|
|
}
|
|
|
|
TEST_F(Base94OutputStreamTest, Encoding10) {
|
|
std::vector<uint8_t> binary = {0xFF};
|
|
std::string text("d#");
|
|
RunTest(text, binary);
|
|
}
|
|
|
|
TEST_F(Base94OutputStreamTest, Encoding11) {
|
|
std::vector<uint8_t> binary = {0xFF, 0xFF};
|
|
std::string text(".x(");
|
|
RunTest(text, binary);
|
|
}
|
|
|
|
TEST_F(Base94OutputStreamTest, Encoding12) {
|
|
std::vector<uint8_t> binary = {0xFF, 0xFF, 0xFF};
|
|
std::string text(".xj6");
|
|
RunTest(text, binary);
|
|
}
|
|
|
|
TEST_F(Base94OutputStreamTest, Encoding13) {
|
|
std::vector<uint8_t> binary = {0xFF, 0xFF, 0xFF, 0xFF};
|
|
std::string text(".x.x`");
|
|
RunTest(text, binary);
|
|
}
|
|
|
|
TEST_F(Base94OutputStreamTest, Encoding14) {
|
|
std::vector<uint8_t> binary = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
|
std::string text(".x.x.x\"");
|
|
RunTest(text, binary);
|
|
}
|
|
|
|
TEST_F(Base94OutputStreamTest, Printable94) {
|
|
std::vector<uint8_t> binary = {
|
|
0x5e, 0x0, 0x47, 0xa0, 0x1d, 0x60, 0xa, 0xab, 0x41, 0x41, 0xa4, 0x9,
|
|
0x64, 0x71, 0x32, 0xc, 0x47, 0xf9, 0x20, 0x22, 0xa3, 0x44, 0xa0, 0x84,
|
|
0x15, 0xe0, 0xf2, 0x61, 0xfc, 0x4c, 0xb7, 0xe1, 0x39, 0x9b, 0x47, 0xff,
|
|
0x64, 0x21, 0x5c, 0x74, 0x91, 0xec, 0x52, 0x75, 0xa2, 0x51, 0x93, 0x4a,
|
|
0x5e, 0x45, 0x2d, 0xd8, 0xf5, 0xc0, 0xdc, 0x58, 0x33, 0x63, 0x69, 0x8b,
|
|
0x4d, 0xbd, 0x25, 0x39, 0x54, 0x77, 0xf0, 0xcc, 0x5e, 0xf1, 0x23, 0x81,
|
|
0x6, 0x21, 0x71, 0x28, 0x28, 0x2};
|
|
std::string text(
|
|
"!\"#$%&'()*+,-./"
|
|
"0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`"
|
|
"abcdefghijklmnopqrstuvwxyz{|}~!");
|
|
RunTest(text, binary);
|
|
}
|
|
|
|
TEST_F(Base94OutputStreamTest, WriteLongDataMultipleTimes) {
|
|
const uint8_t* input = BuildRandomInput(kLongDataLength);
|
|
SCOPED_TRACE(base::StringPrintf("Input: %s",
|
|
DumpInput(input, kLongDataLength).c_str()));
|
|
// Call Write() a random number of times.
|
|
size_t index = 0;
|
|
while (index < kLongDataLength) {
|
|
size_t write_length =
|
|
std::min(static_cast<size_t>(base::RandInt(0, 4096 * 2)),
|
|
kLongDataLength - index);
|
|
SCOPED_TRACE(
|
|
base::StringPrintf("index %zu, write_length %zu", index, write_length));
|
|
EXPECT_TRUE(round_trip()->Write(input + index, write_length));
|
|
index += write_length;
|
|
}
|
|
EXPECT_TRUE(round_trip()->Flush());
|
|
EXPECT_EQ(round_trip_test_output_stream().all_data().size(), kLongDataLength);
|
|
EXPECT_EQ(memcmp(round_trip_test_output_stream().all_data().data(),
|
|
input,
|
|
kLongDataLength),
|
|
0);
|
|
}
|
|
|
|
TEST_F(Base94OutputStreamTest, WriteDeterministicLongDataMultipleTimes) {
|
|
const uint8_t* input = BuildDeterministicInput(kLongDataLength);
|
|
|
|
static constexpr size_t kWriteLengths[] = {
|
|
4, 96, 40, kLongDataLength - 4 - 96 - 40};
|
|
|
|
size_t offset = 0;
|
|
for (size_t index = 0; index < base::size(kWriteLengths); ++index) {
|
|
const size_t write_length = kWriteLengths[index];
|
|
SCOPED_TRACE(base::StringPrintf(
|
|
"offset %zu, write_length %zu", offset, write_length));
|
|
EXPECT_TRUE(round_trip()->Write(input + offset, write_length));
|
|
offset += write_length;
|
|
}
|
|
EXPECT_TRUE(round_trip()->Flush());
|
|
EXPECT_EQ(round_trip_test_output_stream().all_data().size(), kLongDataLength);
|
|
EXPECT_EQ(memcmp(round_trip_test_output_stream().all_data().data(),
|
|
input,
|
|
kLongDataLength),
|
|
0);
|
|
}
|
|
|
|
TEST_F(Base94OutputStreamTest, NoWriteOrFlush) {
|
|
EXPECT_EQ(round_trip_test_output_stream().write_count(), 0u);
|
|
EXPECT_EQ(round_trip_test_output_stream().flush_count(), 0u);
|
|
EXPECT_TRUE(round_trip_test_output_stream().all_data().empty());
|
|
}
|
|
|
|
TEST_F(Base94OutputStreamTest, FlushWithoutWrite) {
|
|
EXPECT_TRUE(round_trip()->Flush());
|
|
EXPECT_EQ(round_trip_test_output_stream().write_count(), 0u);
|
|
EXPECT_EQ(round_trip_test_output_stream().flush_count(), 1u);
|
|
EXPECT_TRUE(round_trip_test_output_stream().all_data().empty());
|
|
}
|
|
|
|
TEST_F(Base94OutputStreamTest, WriteEmptyData) {
|
|
std::vector<uint8_t> empty_data;
|
|
EXPECT_TRUE(round_trip()->Write(
|
|
static_cast<const uint8_t*>(empty_data.data()), empty_data.size()));
|
|
EXPECT_TRUE(round_trip()->Flush());
|
|
EXPECT_TRUE(round_trip()->Flush());
|
|
EXPECT_EQ(round_trip_test_output_stream().write_count(), 0u);
|
|
EXPECT_EQ(round_trip_test_output_stream().flush_count(), 2u);
|
|
EXPECT_TRUE(round_trip_test_output_stream().all_data().empty());
|
|
}
|
|
|
|
TEST_F(Base94OutputStreamTest, Process7bitsInFinishDecoding) {
|
|
std::vector<uint8_t> input = {
|
|
0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
EXPECT_TRUE(round_trip()->Write(static_cast<const uint8_t*>(input.data()),
|
|
input.size()));
|
|
EXPECT_TRUE(round_trip()->Flush());
|
|
VerifyRoundTrip(input);
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace test
|
|
} // namespace crashpad
|