summaryrefslogtreecommitdiffstats
path: root/third_party/zlib
diff options
context:
space:
mode:
authorhaven@chromium.org <haven@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-17 00:36:28 +0000
committerhaven@chromium.org <haven@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-17 00:36:28 +0000
commitec6524fe9b25cea3c1bfa05d8b816966edea0d88 (patch)
treebf73b45525eedbf916beaed50f5b17d7fa9955d5 /third_party/zlib
parentf791d5dc5945d2410264922389399b9300d7f256 (diff)
downloadchromium_src-ec6524fe9b25cea3c1bfa05d8b816966edea0d88.zip
chromium_src-ec6524fe9b25cea3c1bfa05d8b816966edea0d88.tar.gz
chromium_src-ec6524fe9b25cea3c1bfa05d8b816966edea0d88.tar.bz2
Adds asynchronous unzip functions to ZipReader
Updates ImageWriterPrivate to use the new asynchronous functions. BUG=324091 Review URL: https://codereview.chromium.org/92873003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@245393 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'third_party/zlib')
-rw-r--r--third_party/zlib/google/zip_reader.cc112
-rw-r--r--third_party/zlib/google/zip_reader.h33
-rw-r--r--third_party/zlib/google/zip_reader_unittest.cc141
3 files changed, 276 insertions, 10 deletions
diff --git a/third_party/zlib/google/zip_reader.cc b/third_party/zlib/google/zip_reader.cc
index 8787254..7b7870e 100644
--- a/third_party/zlib/google/zip_reader.cc
+++ b/third_party/zlib/google/zip_reader.cc
@@ -6,6 +6,7 @@
#include "base/file_util.h"
#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "net/base/file_stream.h"
@@ -68,7 +69,8 @@ ZipReader::EntryInfo::EntryInfo(const std::string& file_name_in_zip,
}
}
-ZipReader::ZipReader() {
+ZipReader::ZipReader()
+ : weak_ptr_factory_(this) {
Reset();
}
@@ -241,6 +243,65 @@ bool ZipReader::ExtractCurrentEntryToFilePath(
return success;
}
+void ZipReader::ExtractCurrentEntryToFilePathAsync(
+ const base::FilePath& output_file_path,
+ const SuccessCallback& success_callback,
+ const FailureCallback& failure_callback,
+ const ProgressCallback& progress_callback) {
+ DCHECK(zip_file_);
+ DCHECK(current_entry_info_.get());
+
+ // If this is a directory, just create it and return.
+ if (current_entry_info()->is_directory()) {
+ if (base::CreateDirectory(output_file_path)) {
+ base::MessageLoopProxy::current()->PostTask(FROM_HERE, success_callback);
+ } else {
+ DVLOG(1) << "Unzip failed: unable to create directory.";
+ base::MessageLoopProxy::current()->PostTask(FROM_HERE, failure_callback);
+ }
+ return;
+ }
+
+ if (unzOpenCurrentFile(zip_file_) != UNZ_OK) {
+ DVLOG(1) << "Unzip failed: unable to open current zip entry.";
+ base::MessageLoopProxy::current()->PostTask(FROM_HERE, failure_callback);
+ return;
+ }
+
+ base::FilePath output_dir_path = output_file_path.DirName();
+ if (!base::CreateDirectory(output_dir_path)) {
+ DVLOG(1) << "Unzip failed: unable to create containing directory.";
+ base::MessageLoopProxy::current()->PostTask(FROM_HERE, failure_callback);
+ return;
+ }
+
+ const int flags = (base::PLATFORM_FILE_CREATE_ALWAYS |
+ base::PLATFORM_FILE_WRITE);
+ bool created = false;
+ base::PlatformFileError platform_file_error;
+ base::PlatformFile output_file = CreatePlatformFile(output_file_path,
+ flags,
+ &created,
+ &platform_file_error);
+
+ if (platform_file_error != base::PLATFORM_FILE_OK) {
+ DVLOG(1) << "Unzip failed: unable to create platform file at "
+ << output_file_path.value();
+ base::MessageLoopProxy::current()->PostTask(FROM_HERE, failure_callback);
+ return;
+ }
+
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&ZipReader::ExtractChunk,
+ weak_ptr_factory_.GetWeakPtr(),
+ output_file,
+ success_callback,
+ failure_callback,
+ progress_callback,
+ 0 /* initial offset */));
+}
+
bool ZipReader::ExtractCurrentEntryIntoDirectory(
const base::FilePath& output_directory_path) {
DCHECK(current_entry_info_.get());
@@ -313,4 +374,53 @@ void ZipReader::Reset() {
current_entry_info_.reset();
}
+void ZipReader::ExtractChunk(base::PlatformFile output_file,
+ const SuccessCallback& success_callback,
+ const FailureCallback& failure_callback,
+ const ProgressCallback& progress_callback,
+ const int64 offset) {
+ char buffer[internal::kZipBufSize];
+
+ const int num_bytes_read = unzReadCurrentFile(zip_file_,
+ buffer,
+ internal::kZipBufSize);
+
+ if (num_bytes_read == 0) {
+ unzCloseCurrentFile(zip_file_);
+ base::ClosePlatformFile(output_file);
+ success_callback.Run();
+ } else if (num_bytes_read < 0) {
+ DVLOG(1) << "Unzip failed: error while reading zipfile "
+ << "(" << num_bytes_read << ")";
+ base::ClosePlatformFile(output_file);
+ failure_callback.Run();
+ } else {
+ if (num_bytes_read != base::WritePlatformFile(output_file,
+ offset,
+ buffer,
+ num_bytes_read)) {
+ DVLOG(1) << "Unzip failed: unable to write all bytes to target.";
+ base::ClosePlatformFile(output_file);
+ failure_callback.Run();
+ return;
+ }
+
+ int64 current_progress = offset + num_bytes_read;
+
+ progress_callback.Run(current_progress);
+
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&ZipReader::ExtractChunk,
+ weak_ptr_factory_.GetWeakPtr(),
+ output_file,
+ success_callback,
+ failure_callback,
+ progress_callback,
+ current_progress));
+
+ }
+}
+
+
} // namespace zip
diff --git a/third_party/zlib/google/zip_reader.h b/third_party/zlib/google/zip_reader.h
index a3e1ee8..5f0a167 100644
--- a/third_party/zlib/google/zip_reader.h
+++ b/third_party/zlib/google/zip_reader.h
@@ -1,16 +1,17 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-
#ifndef THIRD_PARTY_ZLIB_GOOGLE_ZIP_READER_H_
#define THIRD_PARTY_ZLIB_GOOGLE_ZIP_READER_H_
#include <string>
#include "base/basictypes.h"
+#include "base/callback.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "base/platform_file.h"
#include "base/time/time.h"
@@ -42,6 +43,14 @@ namespace zip {
//
class ZipReader {
public:
+ // A callback that is called when the operation is successful.
+ typedef base::Closure SuccessCallback;
+ // A callback that is called when the operation fails.
+ typedef base::Closure FailureCallback;
+ // A callback that is called periodically during the operation with the number
+ // of bytes that have been processed so far.
+ typedef base::Callback<void(int64)> ProgressCallback;
+
// This class represents information of an entry (file or directory) in
// a zip file.
class EntryInfo {
@@ -139,6 +148,18 @@ class ZipReader {
// timestamp is not valid, the timestamp will be set to the current time.
bool ExtractCurrentEntryToFilePath(const base::FilePath& output_file_path);
+ // Asynchronously extracts the current entry to the given output file path.
+ // If the current entry is a directory it just creates the directory
+ // synchronously instead. OpenCurrentEntryInZip() must be called beforehand.
+ // success_callback will be called on success and failure_callback will be
+ // called on failure. progress_callback will be called at least once.
+ // Callbacks will be posted to the current MessageLoop in-order.
+ void ExtractCurrentEntryToFilePathAsync(
+ const base::FilePath& output_file_path,
+ const SuccessCallback& success_callback,
+ const FailureCallback& failure_callback,
+ const ProgressCallback& progress_callback);
+
// Extracts the current entry to the given output directory path using
// ExtractCurrentEntryToFilePath(). Sub directories are created as needed
// based on the file path of the current entry. For example, if the file
@@ -176,11 +197,21 @@ class ZipReader {
// Resets the internal state.
void Reset();
+ // Extracts a chunk of the file to the target. Will post a task for the next
+ // chunk and success/failure/progress callbacks as necessary.
+ void ExtractChunk(base::PlatformFile target_file,
+ const SuccessCallback& success_callback,
+ const FailureCallback& failure_callback,
+ const ProgressCallback& progress_callback,
+ const int64 offset);
+
unzFile zip_file_;
int num_entries_;
bool reached_end_;
scoped_ptr<EntryInfo> current_entry_info_;
+ base::WeakPtrFactory<ZipReader> weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(ZipReader);
};
diff --git a/third_party/zlib/google/zip_reader_unittest.cc b/third_party/zlib/google/zip_reader_unittest.cc
index 09efd9a..2033f9f 100644
--- a/third_party/zlib/google/zip_reader_unittest.cc
+++ b/third_party/zlib/google/zip_reader_unittest.cc
@@ -7,12 +7,14 @@
#include <set>
#include <string>
+#include "base/bind.h"
#include "base/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
#include "base/md5.h"
#include "base/path_service.h"
#include "base/platform_file.h"
+#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -21,6 +23,8 @@
namespace {
+const static std::string kQuuxExpectedMD5 = "d1ae4ac8a17a0e09317113ab284b57a6";
+
// Wrap PlatformFiles in a class so that we don't leak them in tests.
class PlatformFileWrapper {
public:
@@ -61,6 +65,48 @@ class PlatformFileWrapper {
base::PlatformFile file_;
};
+// A mock that provides methods that can be used as callbacks in asynchronous
+// unzip functions. Tracks the number of calls and number of bytes reported.
+// Assumes that progress callbacks will be executed in-order.
+class MockUnzipListener : public base::SupportsWeakPtr<MockUnzipListener> {
+ public:
+ MockUnzipListener()
+ : success_calls_(0),
+ failure_calls_(0),
+ progress_calls_(0),
+ current_progress_(0) {
+ }
+
+ // Success callback for async functions.
+ void OnUnzipSuccess() {
+ success_calls_++;
+ }
+
+ // Failure callback for async functions.
+ void OnUnzipFailure() {
+ failure_calls_++;
+ }
+
+ // Progress callback for async functions.
+ void OnUnzipProgress(int64 progress) {
+ DCHECK(progress > current_progress_);
+ progress_calls_++;
+ current_progress_ = progress;
+ }
+
+ int success_calls() { return success_calls_; }
+ int failure_calls() { return failure_calls_; }
+ int progress_calls() { return progress_calls_; }
+ int current_progress() { return current_progress_; }
+
+ private:
+ int success_calls_;
+ int failure_calls_;
+ int progress_calls_;
+
+ int64 current_progress_;
+};
+
} // namespace
namespace zip {
@@ -113,6 +159,16 @@ class ZipReaderTest : public PlatformTest {
return true;
}
+ bool CompareFileAndMD5(const base::FilePath& path,
+ const std::string expected_md5) {
+ // Read the output file and compute the MD5.
+ std::string output;
+ if (!base::ReadFileToString(path, &output))
+ return false;
+ const std::string md5 = base::MD5String(output);
+ return expected_md5 == md5;
+ }
+
// The path to temporary directory used to contain the test operations.
base::FilePath test_dir_;
// The path to the test data directory where test.zip etc. are located.
@@ -128,6 +184,8 @@ class ZipReaderTest : public PlatformTest {
std::set<base::FilePath> test_zip_contents_;
base::ScopedTempDir temp_dir_;
+
+ base::MessageLoop message_loop_;
};
TEST_F(ZipReaderTest, Open_ValidZipFile) {
@@ -220,8 +278,7 @@ TEST_F(ZipReaderTest, ExtractCurrentEntryToFilePath_RegularFile) {
ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("quux.txt"),
&output));
const std::string md5 = base::MD5String(output);
- const std::string kExpectedMD5 = "d1ae4ac8a17a0e09317113ab284b57a6";
- EXPECT_EQ(kExpectedMD5, md5);
+ EXPECT_EQ(kQuuxExpectedMD5, md5);
// quux.txt should be larger than kZipBufSize so that we can exercise
// the loop in ExtractCurrentEntry().
EXPECT_LT(static_cast<size_t>(internal::kZipBufSize), output.size());
@@ -241,8 +298,7 @@ TEST_F(ZipReaderTest, PlatformFileExtractCurrentEntryToFilePath_RegularFile) {
ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("quux.txt"),
&output));
const std::string md5 = base::MD5String(output);
- const std::string kExpectedMD5 = "d1ae4ac8a17a0e09317113ab284b57a6";
- EXPECT_EQ(kExpectedMD5, md5);
+ EXPECT_EQ(kQuuxExpectedMD5, md5);
// quux.txt should be larger than kZipBufSize so that we can exercise
// the loop in ExtractCurrentEntry().
EXPECT_LT(static_cast<size_t>(internal::kZipBufSize), output.size());
@@ -264,8 +320,7 @@ TEST_F(ZipReaderTest, PlatformFileExtractCurrentEntryToFd_RegularFile) {
ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("quux.txt"),
&output));
const std::string md5 = base::MD5String(output);
- const std::string kExpectedMD5 = "d1ae4ac8a17a0e09317113ab284b57a6";
- EXPECT_EQ(kExpectedMD5, md5);
+ EXPECT_EQ(kQuuxExpectedMD5, md5);
// quux.txt should be larger than kZipBufSize so that we can exercise
// the loop in ExtractCurrentEntry().
EXPECT_LT(static_cast<size_t>(internal::kZipBufSize), output.size());
@@ -296,8 +351,7 @@ TEST_F(ZipReaderTest, ExtractCurrentEntryIntoDirectory_RegularFile) {
ASSERT_TRUE(base::ReadFileToString(
test_dir_.AppendASCII("foo/bar/quux.txt"), &output));
const std::string md5 = base::MD5String(output);
- const std::string kExpectedMD5 = "d1ae4ac8a17a0e09317113ab284b57a6";
- EXPECT_EQ(kExpectedMD5, md5);
+ EXPECT_EQ(kQuuxExpectedMD5, md5);
}
TEST_F(ZipReaderTest, current_entry_info_RegularFile) {
@@ -428,4 +482,75 @@ TEST_F(ZipReaderTest, OpenFromString) {
EXPECT_EQ(std::string("This is a test.\n"), actual);
}
+// Verifies that the asynchronous extraction to a file works.
+TEST_F(ZipReaderTest, ExtractToFileAsync_RegularFile) {
+ MockUnzipListener listener;
+
+ ZipReader reader;
+ base::FilePath target_file = test_dir_.AppendASCII("quux.txt");
+ base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
+ ASSERT_TRUE(reader.Open(test_zip_file_));
+ ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
+ reader.ExtractCurrentEntryToFilePathAsync(
+ target_file,
+ base::Bind(&MockUnzipListener::OnUnzipSuccess,
+ listener.AsWeakPtr()),
+ base::Bind(&MockUnzipListener::OnUnzipFailure,
+ listener.AsWeakPtr()),
+ base::Bind(&MockUnzipListener::OnUnzipProgress,
+ listener.AsWeakPtr()));
+
+ EXPECT_EQ(0, listener.success_calls());
+ EXPECT_EQ(0, listener.failure_calls());
+ EXPECT_EQ(0, listener.progress_calls());
+
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(1, listener.success_calls());
+ EXPECT_EQ(0, listener.failure_calls());
+ EXPECT_LE(1, listener.progress_calls());
+
+ std::string output;
+ ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("quux.txt"),
+ &output));
+ const std::string md5 = base::MD5String(output);
+ EXPECT_EQ(kQuuxExpectedMD5, md5);
+
+ int64 file_size = 0;
+ ASSERT_TRUE(base::GetFileSize(target_file, &file_size));
+
+ EXPECT_EQ(file_size, listener.current_progress());
+}
+
+// Verifies that the asynchronous extraction to a file works.
+TEST_F(ZipReaderTest, ExtractToFileAsync_Directory) {
+ MockUnzipListener listener;
+
+ ZipReader reader;
+ base::FilePath target_file = test_dir_.AppendASCII("foo");
+ base::FilePath target_path(FILE_PATH_LITERAL("foo/"));
+ ASSERT_TRUE(reader.Open(test_zip_file_));
+ ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
+ reader.ExtractCurrentEntryToFilePathAsync(
+ target_file,
+ base::Bind(&MockUnzipListener::OnUnzipSuccess,
+ listener.AsWeakPtr()),
+ base::Bind(&MockUnzipListener::OnUnzipFailure,
+ listener.AsWeakPtr()),
+ base::Bind(&MockUnzipListener::OnUnzipProgress,
+ listener.AsWeakPtr()));
+
+ EXPECT_EQ(0, listener.success_calls());
+ EXPECT_EQ(0, listener.failure_calls());
+ EXPECT_EQ(0, listener.progress_calls());
+
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(1, listener.success_calls());
+ EXPECT_EQ(0, listener.failure_calls());
+ EXPECT_GE(0, listener.progress_calls());
+
+ ASSERT_TRUE(base::DirectoryExists(target_file));
+}
+
} // namespace zip