Skip to content

Feature/zlibwrapper fixes #355

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions app/rest/tests/zlibwrapper_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <iostream>
#include <memory>
#include <string>
#include <string_view>
#include <vector>

#include "gtest/gtest.h"
Expand Down Expand Up @@ -1047,5 +1048,135 @@ TEST(ZLibWrapperStandalone, ReadPastEndOfWindow) {
LOG(INFO) << "passed read-past-end-of-window test";
}

TEST(ZLibWrapperStandalone, BytewiseRead) {
std::string text =
"v nedrah tundry vydra v getrah tyrit v vedrah yadra kedra";
size_t text_len = text.size();
size_t archive_len = ZLib::MinCompressbufSize(text_len);
std::string archive(archive_len, '\0');
size_t decompressed_len = text_len + 1;
std::string decompressed(decompressed_len, '\0');
size_t decompressed_offset = 0;

ZLib compressor;
compressor.SetGzipHeaderMode();
int rc = compressor.Compress((Bytef*)archive.data(), &archive_len,
(Bytef*)text.data(), text_len);
ASSERT_EQ(rc, Z_OK);

ZLib zlib;
zlib.SetGzipHeaderMode();
for (size_t i = 0; i < archive_len; ++i) {
size_t source_len = 1;
size_t dest_len = decompressed_len - decompressed_offset;
rc = zlib.UncompressAtMost(
(Bytef*)decompressed.data() + decompressed_offset, &dest_len,
(Bytef*)archive.data() + i, &source_len);
ASSERT_EQ(rc, Z_OK);
ASSERT_EQ(source_len, 0);
decompressed_offset += dest_len;
}

ASSERT_TRUE(zlib.IsGzipFooterValid());
ASSERT_EQ(decompressed_offset, text_len);

std::string truncated_output(decompressed.data(), text_len);
ASSERT_EQ(truncated_output, text);

// if we haven't segfaulted by now, we pass
LOG(INFO) << "passed bytewise-read test";
}

TEST(ZLibWrapperStandaloneTest, TruncatedData) {
const int kBufferLen = 64;
std::string uncompressed = "Hello, World!";
std::string compressed(
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\xf3\x48\xcd\xc9\xc9"
"\xd7\x51\x08\xcf\x2f\xca\x49\x51\x04\x00\xd0\xc3\x4a\xec\x0d"
"\x00\x00\x00",
33);

// Verify that "compressed" contains valid gzip data.
{
ZLib zlib;
zlib.SetGzipHeaderMode();
char uncompbuf[kBufferLen];
bzero(uncompbuf, kBufferLen);
uLongf uncomplen = kBufferLen;
int err = zlib.Uncompress(
reinterpret_cast<Bytef*>(uncompbuf), &uncomplen,
reinterpret_cast<const Bytef*>(compressed.c_str()), compressed.size());
ASSERT_EQ(err, Z_OK);
ASSERT_EQ(uncompressed, std::string_view(uncompbuf, uncomplen));
}

// Test truncated data with ZLib::Uncompress().
for (int len = compressed.size() - 1; len > 0; len--) {
SCOPED_TRACE(absl::StrCat("Decompressing first ", len, " out of ",
compressed.size(), " bytes"));
ZLib zlib;
zlib.SetGzipHeaderMode();
char uncompbuf[kBufferLen];
bzero(uncompbuf, kBufferLen);
uLongf uncomplen = kBufferLen;
int err = zlib.Uncompress(
reinterpret_cast<Bytef*>(uncompbuf), &uncomplen,
reinterpret_cast<const Bytef*>(compressed.c_str()), len);
ASSERT_NE(err, Z_OK);
}

// Test truncated data with ZLib::UncompressAtMost() and
// ZLib::UncompressDone().
for (int len = compressed.size() - 1; len > 0; len--) {
SCOPED_TRACE(absl::StrCat("Decompressing first ", len, " out of ",
compressed.size(), " bytes"));
ZLib zlib;
zlib.SetGzipHeaderMode();
char uncompbuf[kBufferLen];
bzero(uncompbuf, kBufferLen);
uLongf uncomplen = kBufferLen;
uLongf complen = len;
int err = zlib.UncompressAtMost(
reinterpret_cast<Bytef*>(uncompbuf), &uncomplen,
reinterpret_cast<const Bytef*>(compressed.c_str()), &complen);
ASSERT_EQ(err, Z_OK);
ASSERT_EQ(complen, 0);
if (uncomplen > 0) {
EXPECT_THAT(uncompressed,
testing::StartsWith(absl::string_view(uncompbuf, uncomplen)));
}
ASSERT_FALSE(zlib.UncompressChunkDone());
}
}

TEST(ZLibWrapperStandalone, GzipUncompressedLength) {
ZLib zlib;

// "Hello, World!", compressed.
std::string hello_world(
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\xf3\x48\xcd\xc9\xc9"
"\xd7\x51\x08\xcf\x2f\xca\x49\x51\x04\x00\xd0\xc3\x4a\xec\x0d"
"\x00\x00\x00",
33);
EXPECT_EQ(13, zlib.GzipUncompressedLength(
reinterpret_cast<const Bytef*>(hello_world.c_str()),
hello_world.size()));

// Empty string, "", compressed.
std::string empty(
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\x03\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00",
20);
EXPECT_EQ(0,
zlib.GzipUncompressedLength(
reinterpret_cast<const Bytef*>(empty.c_str()), empty.size()));

std::string bad_data("\x01\x01\x01\x01", 4);
for (int len = 0; len <= bad_data.size(); len++) {
EXPECT_EQ(0, zlib.GzipUncompressedLength(
reinterpret_cast<const Bytef*>(bad_data.c_str()), len));
}
}

} // namespace
} // namespace firebase
7 changes: 5 additions & 2 deletions app/rest/zlibwrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,9 @@ int ZLib::UncompressChunk(Bytef* dest, uLongf* destLen, const Bytef* source,
// mode, we also check the gzip footer to make sure we pass the gzip
// consistency checks. We RETURN true iff both types of checks pass.
bool ZLib::UncompressChunkDone() {
assert(!first_chunk_ && uncomp_init_);
if (first_chunk_ || !uncomp_init_) {
return false;
}
// Make sure we're at the end-of-compressed-data point. This means
// if we call inflate with Z_FINISH we won't consume any input or
// write any output
Expand Down Expand Up @@ -784,7 +786,8 @@ int ZLib::Uncompress(Bytef* dest, uLongf* destLen, const Bytef* source,

// read uncompress length from gzip footer
uLongf ZLib::GzipUncompressedLength(const Bytef* source, uLong len) {
assert(len > 4);
if (len <= 4) return 0; // malformed data.

return (static_cast<uLongf>(source[len - 1]) << 24) +
(static_cast<uLongf>(source[len - 2]) << 16) +
(static_cast<uLongf>(source[len - 3]) << 8) +
Expand Down
3 changes: 2 additions & 1 deletion app/rest/zlibwrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ class ZLib {
int Uncompress(Bytef* dest, uLongf* destLen, const Bytef* source,
uLong sourceLen);

// Get the uncompressed size from the gzip header.
// Get the uncompressed size from the gzip header. Returns 0 if source is too
// short (len < 5).
uLongf GzipUncompressedLength(const Bytef* source, uLong len);

// Special helper function to help uncompress gzipped documents:
Expand Down