Commit aeea2725 authored by joaoe@opera.com's avatar joaoe@opera.com

New ZipReader::ExtractCurrentEntryToString API.

New API which extract a zip entry into a memory buffer.

BUG=359428

Review URL: https://codereview.chromium.org/292443006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278766 0039d316-1c4b-4281-b951-d872f2087c98
parent 72fbe9a8
......@@ -344,6 +344,58 @@ bool ZipReader::ExtractCurrentEntryToFd(const int fd) {
}
#endif // defined(OS_POSIX)
bool ZipReader::ExtractCurrentEntryToString(
size_t max_read_bytes,
std::string* output) const {
DCHECK(output);
DCHECK(zip_file_);
DCHECK(max_read_bytes != 0);
if (current_entry_info()->is_directory()) {
output->clear();
return true;
}
const int open_result = unzOpenCurrentFile(zip_file_);
if (open_result != UNZ_OK)
return false;
// The original_size() is the best hint for the real size, so it saves
// doing reallocations for the common case when the uncompressed size is
// correct. However, we need to assume that the uncompressed size could be
// incorrect therefore this function needs to read as much data as possible.
std::string contents;
contents.reserve(std::min<size_t>(
max_read_bytes, current_entry_info()->original_size()));
bool success = true; // This becomes false when something bad happens.
char buf[internal::kZipBufSize];
while (true) {
const int num_bytes_read = unzReadCurrentFile(zip_file_, buf,
internal::kZipBufSize);
if (num_bytes_read == 0) {
// Reached the end of the file.
break;
} else if (num_bytes_read < 0) {
// If num_bytes_read < 0, then it's a specific UNZ_* error code.
success = false;
break;
} else if (num_bytes_read > 0) {
if (contents.size() + num_bytes_read > max_read_bytes) {
success = false;
break;
}
contents.append(buf, num_bytes_read);
}
}
unzCloseCurrentFile(zip_file_);
if (success)
output->swap(contents);
return success;
}
bool ZipReader::OpenInternal() {
DCHECK(zip_file_);
......
......@@ -35,7 +35,7 @@ namespace zip {
// reader.AdvanceToNextEntry();
// }
//
// For simplicty, error checking is omitted in the example code above. The
// For simplicity, error checking is omitted in the example code above. The
// production code should check return values from all of these functions.
//
// This calls can also be used for random access of contents in a zip file
......@@ -182,6 +182,21 @@ class ZipReader {
bool ExtractCurrentEntryToFd(int fd);
#endif
// Extracts the current entry into memory. If the current entry is a directory
// the |output| parameter is set to the empty string. If the current entry is
// a file, the |output| parameter is filled with its contents. Returns true on
// success. OpenCurrentEntryInZip() must be called beforehand.
// Note: the |output| parameter can be filled with a big amount of data, avoid
// passing it around by value, but by reference or pointer.
// Note: the value returned by EntryInfo::original_size() cannot be
// trusted, so the real size of the uncompressed contents can be different.
// Use max_read_bytes to limit the ammount of memory used to carry the entry.
// If the real size of the uncompressed data is bigger than max_read_bytes
// then false is returned. |max_read_bytes| must be non-zero.
bool ExtractCurrentEntryToString(
size_t max_read_bytes,
std::string* output) const;
// Returns the current entry info. Returns NULL if the current entry is
// not yet opened. OpenCurrentEntryInZip() must be called beforehand.
EntryInfo* current_entry_info() const {
......
......@@ -15,6 +15,7 @@
#include "base/md5.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -534,4 +535,42 @@ TEST_F(ZipReaderTest, ExtractToFileAsync_Directory) {
ASSERT_TRUE(base::DirectoryExists(target_file));
}
TEST_F(ZipReaderTest, ExtractCurrentEntryToString) {
// test_mismatch_size.zip contains files with names from 0.txt to 7.txt with
// sizes from 0 to 7 bytes respectively, being the contents of each file a
// substring of "0123456" starting at '0'.
base::FilePath test_zip_file =
test_data_dir_.AppendASCII("test_mismatch_size.zip");
ZipReader reader;
std::string contents;
ASSERT_TRUE(reader.Open(test_zip_file));
for (size_t i = 0; i < 8; i++) {
SCOPED_TRACE(base::StringPrintf("Processing %d.txt", static_cast<int>(i)));
base::FilePath file_name = base::FilePath::FromUTF8Unsafe(
base::StringPrintf("%d.txt", static_cast<int>(i)));
ASSERT_TRUE(reader.LocateAndOpenEntry(file_name));
if (i > 1) {
// Off by one byte read limit: must fail.
EXPECT_FALSE(reader.ExtractCurrentEntryToString(i - 1, &contents));
}
if (i > 0) {
// Exact byte read limit: must pass.
EXPECT_TRUE(reader.ExtractCurrentEntryToString(i, &contents));
EXPECT_EQ(i, contents.size());
EXPECT_EQ(0, memcmp(contents.c_str(), "0123456", i));
}
// More than necessary byte read limit: must pass.
EXPECT_TRUE(reader.ExtractCurrentEntryToString(16, &contents));
EXPECT_EQ(i, contents.size());
EXPECT_EQ(0, memcmp(contents.c_str(), "0123456", i));
}
reader.Close();
}
} // namespace zip
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment