From f65c4fef950feb2f5f280e769bb06f108e1fa6d2 Mon Sep 17 00:00:00 2001 From: Alex Zolotarev Date: Sat, 2 Jul 2011 20:14:34 +0300 Subject: [PATCH] Added zip reader support (only works with non-compressed files) --- coding/coding.pro | 4 +- coding/coding_tests/coding_tests.pro | 1 + coding/coding_tests/zip_reader_test.cpp | 70 +++++++++++++++++++++++++ coding/file_reader.cpp | 7 +++ coding/file_reader.hpp | 4 ++ coding/zip_reader.cpp | 21 +++++--- coding/zip_reader.hpp | 17 ++++++ 7 files changed, 115 insertions(+), 9 deletions(-) create mode 100644 coding/coding_tests/zip_reader_test.cpp create mode 100644 coding/zip_reader.hpp diff --git a/coding/coding.pro b/coding/coding.pro index efd20bc090..38ed8ec6ff 100644 --- a/coding/coding.pro +++ b/coding/coding.pro @@ -23,7 +23,8 @@ SOURCES += \ base64.cpp \ sha2.cpp \ multilang_utf8_string.cpp \ - reader.cpp + reader.cpp \ + zip_reader.cpp \ HEADERS += \ internal/xmlparser.h \ @@ -81,3 +82,4 @@ HEADERS += \ value_opt_string.hpp \ multilang_utf8_string.hpp \ url_encode.hpp \ + zip_reader.hpp \ diff --git a/coding/coding_tests/coding_tests.pro b/coding/coding_tests/coding_tests.pro index 8087079fa0..12a96f2aad 100644 --- a/coding/coding_tests/coding_tests.pro +++ b/coding/coding_tests/coding_tests.pro @@ -37,6 +37,7 @@ SOURCES += ../../testing/testingmain.cpp \ value_opt_string_test.cpp \ multilang_utf8_string_test.cpp \ file_data_test.cpp \ + zip_reader_test.cpp \ HEADERS += \ reader_test.hpp \ diff --git a/coding/coding_tests/zip_reader_test.cpp b/coding/coding_tests/zip_reader_test.cpp new file mode 100644 index 0000000000..aa92f6af3b --- /dev/null +++ b/coding/coding_tests/zip_reader_test.cpp @@ -0,0 +1,70 @@ +#include "../../testing/testing.hpp" + +#include "../zip_reader.hpp" +#include "../file_writer.hpp" + +#include "../../base/logging.hpp" +#include "../../base/macros.hpp" + +char const zipBytes[] = "PK\003\004\n\0\0\0\0\0\222\226\342>\302\032" +"x\372\005\0\0\0\005\0\0\0\b\0\034\0te" +"st.txtUT\t\0\003\303>\017N\017" +"?\017Nux\v\0\001\004\365\001\0\0\004P\0" +"\0\0Test\nPK\001\002\036\003\n\0\0" +"\0\0\0\222\226\342>\302\032x\372\005\0\0\0\005" +"\0\0\0\b\0\030\0\0\0\0\0\0\0\0\0\244" +"\201\0\0\0\0test.txtUT\005" +"\0\003\303>\017Nux\v\0\001\004\365\001\0\0" +"\004P\0\0\0PK\005\006\0\0\0\0\001\0\001" +"\0N\0\0\0G\0\0\0\0\0"; + +UNIT_TEST(ZipReaderSmoke) +{ + string const ZIPFILE = "smoke_test.zip"; + { + FileWriter f(ZIPFILE); + f.Write(zipBytes, ARRAY_SIZE(zipBytes) - 1); + } + + bool noException = true; + try + { + ZipFileReader r(ZIPFILE, "test.txt"); + string s; + r.ReadAsString(s); + TEST_EQUAL(s, "Test\n", ("Invalid zip file contents")); + } + catch (FileReader::Exception const & e) + { + noException = false; + LOG(LERROR, (e.what())); + } + TEST(noException, ("Unhandled exception")); + + // invalid zip + noException = true; + try + { + ZipFileReader r("some_nonexisting_filename", "test.txt"); + } + catch (FileReader::Exception const &) + { + noException = false; + } + TEST(!noException, ()); + + // invalid file inside zip + noException = true; + try + { + ZipFileReader r(ZIPFILE, "test"); + } + catch (FileReader::Exception const &) + { + noException = false; + } + TEST(!noException, ()); + + FileWriter::DeleteFileX(ZIPFILE); +} + diff --git a/coding/file_reader.cpp b/coding/file_reader.cpp index eb092dc065..e652fba58b 100644 --- a/coding/file_reader.cpp +++ b/coding/file_reader.cpp @@ -101,3 +101,10 @@ FileReader * FileReader::CreateSubReader(uint64_t pos, uint64_t size) const ASSERT_LESS_OR_EQUAL(pos + size, Size(), (pos, size)); return new FileReader(*this, m_Offset + pos, size); } + +void FileReader::SetOffsetAndSize(uint64_t offset, uint64_t size) +{ + ASSERT_LESS_OR_EQUAL(offset + size, Size(), (offset, size)); + m_Offset = offset; + m_Size = size; +} diff --git a/coding/file_reader.hpp b/coding/file_reader.hpp index 1ed7118b0c..4e2370a8e5 100644 --- a/coding/file_reader.hpp +++ b/coding/file_reader.hpp @@ -22,6 +22,10 @@ public: FileReader SubReader(uint64_t pos, uint64_t size) const; FileReader * CreateSubReader(uint64_t pos, uint64_t size) const; +protected: + // Used in special derived readers. + void SetOffsetAndSize(uint64_t offset, uint64_t size); + private: FileReader(FileReader const & reader, uint64_t offset, uint64_t size); diff --git a/coding/zip_reader.cpp b/coding/zip_reader.cpp index b82e87b854..0849f8c02d 100644 --- a/coding/zip_reader.cpp +++ b/coding/zip_reader.cpp @@ -10,7 +10,7 @@ ZipFileReader::ZipFileReader(string const & container, string const & file) : base_type(container) { - unzFile zip = unzOpen(container.c_str()); + unzFile zip = unzOpen64(container.c_str()); if (!zip) MYTHROW(OpenZipException, ("Can't get zip file handle", container)); @@ -19,18 +19,23 @@ ZipFileReader::ZipFileReader(string const & container, string const & file) if (UNZ_OK != unzLocateFile(zip, file.c_str(), 1)) MYTHROW(LocateZipException, ("Can't locate file inside zip", file)); - unz_file_pos filePos; - if (UNZ_OK != unzGetFilePos(zip, &filePos)) - MYTHROW(LocateZipException, ("Can't locate file offset inside zip", file)); + if (UNZ_OK != unzOpenCurrentFile(zip)) + MYTHROW(LocateZipException, ("Can't open file inside zip", file)); - unz_file_info fileInfo; - if (UNZ_OK != unzGetCurrentFileInfo(zip, &fileInfo, NULL, 0, NULL, 0, NULL, 0)) + uint64_t offset = unzGetCurrentFileZStreamPos64(zip); + unzCloseCurrentFile(zip); + + if (offset > Size()) + MYTHROW(LocateZipException, ("Invalid offset inside zip", file)); + + unz_file_info64 fileInfo; + if (UNZ_OK != unzGetCurrentFileInfo64(zip, &fileInfo, NULL, 0, NULL, 0, NULL, 0)) MYTHROW(LocateZipException, ("Can't get uncompressed file size inside zip", file)); if (fileInfo.compressed_size != fileInfo.uncompressed_size) MYTHROW(InvalidZipException, ("File should be uncompressed inside zip", file)); - LOG(LDEBUG, (file, "offset:", filePos.pos_in_zip_directory, "size:", fileInfo.uncompressed_size)); + LOG(LDEBUG, (file, "offset:", offset, "size:", fileInfo.uncompressed_size)); - SetOffsetAndSize(filePos.pos_in_zip_directory, fileInfo.uncompressed_size); + SetOffsetAndSize(offset, fileInfo.uncompressed_size); } diff --git a/coding/zip_reader.hpp b/coding/zip_reader.hpp new file mode 100644 index 0000000000..4a56ded689 --- /dev/null +++ b/coding/zip_reader.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "file_reader.hpp" + +#include "../base/exception.hpp" + +class ZipFileReader : public FileReader +{ + typedef FileReader base_type; + +public: + DECLARE_EXCEPTION(OpenZipException, OpenException); + DECLARE_EXCEPTION(LocateZipException, OpenException); + DECLARE_EXCEPTION(InvalidZipException, OpenException); + + ZipFileReader(string const & container, string const & file); +};