Loading libziparchive/include/ziparchive/zip_archive.h +9 −0 Original line number Diff line number Diff line Loading @@ -187,6 +187,15 @@ int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr, const std::string_view optional_prefix = "", const std::string_view optional_suffix = ""); /* * Start iterating over all entries of a zip file. Use the matcher functor to * restrict iteration to entry names that make the functor return true. * * Returns 0 on success and negative values on failure. */ int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr, std::function<bool(std::string_view entry_name)> matcher); /* * Advance to the next element in the zipfile in iteration order. * Loading libziparchive/zip_archive.cc +21 −12 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ #include <unistd.h> #include <memory> #include <optional> #include <vector> #if defined(__APPLE__) Loading Loading @@ -675,31 +676,40 @@ static int32_t FindEntry(const ZipArchive* archive, std::string_view entryName, struct IterationHandle { ZipArchive* archive; std::string prefix; std::string suffix; std::function<bool(std::string_view)> matcher; uint32_t position = 0; IterationHandle(ZipArchive* archive, std::string_view in_prefix, std::string_view in_suffix) : archive(archive), prefix(in_prefix), suffix(in_suffix) {} IterationHandle(ZipArchive* archive, std::function<bool(std::string_view)> in_matcher) : archive(archive), matcher(std::move(in_matcher)) {} bool Match(std::string_view entry_name) const { return matcher(entry_name); } }; int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr, const std::string_view optional_prefix, const std::string_view optional_suffix) { if (archive == nullptr || archive->cd_entry_map == nullptr) { ALOGW("Zip: Invalid ZipArchiveHandle"); return kInvalidHandle; } if (optional_prefix.size() > static_cast<size_t>(UINT16_MAX) || optional_suffix.size() > static_cast<size_t>(UINT16_MAX)) { ALOGW("Zip: prefix/suffix too long"); return kInvalidEntryName; } auto matcher = [prefix = std::string(optional_prefix), suffix = std::string(optional_suffix)](std::string_view name) mutable { return android::base::StartsWith(name, prefix) && android::base::EndsWith(name, suffix); }; return StartIteration(archive, cookie_ptr, std::move(matcher)); } int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr, std::function<bool(std::string_view)> matcher) { if (archive == nullptr || archive->cd_entry_map == nullptr) { ALOGW("Zip: Invalid ZipArchiveHandle"); return kInvalidHandle; } archive->cd_entry_map->ResetIteration(); *cookie_ptr = new IterationHandle(archive, optional_prefix, optional_suffix); *cookie_ptr = new IterationHandle(archive, matcher); return 0; } Loading Loading @@ -749,8 +759,7 @@ int32_t Next(void* cookie, ZipEntry* data, std::string_view* name) { auto entry = archive->cd_entry_map->Next(archive->central_directory.GetBasePtr()); while (entry != std::pair<std::string_view, uint64_t>()) { const auto [entry_name, offset] = entry; if (android::base::StartsWith(entry_name, handle->prefix) && android::base::EndsWith(entry_name, handle->suffix)) { if (handle->Match(entry_name)) { const int error = FindEntry(archive, entry_name, offset, data); if (!error && name) { *name = entry_name; Loading libziparchive/zip_archive_test.cc +47 −11 Original line number Diff line number Diff line Loading @@ -227,32 +227,44 @@ TEST(ziparchive, Iteration_std_string_view) { CloseArchive(handle); } static void AssertIterationOrder(const std::string_view prefix, const std::string_view suffix, static void AssertIterationNames(void* iteration_cookie, const std::vector<std::string>& expected_names_sorted) { ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); void* iteration_cookie; ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, prefix, suffix)); ZipEntry data; std::vector<std::string> names; std::string name; for (size_t i = 0; i < expected_names_sorted.size(); ++i) { ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); names.push_back(name); } // End of iteration. ASSERT_EQ(-1, Next(iteration_cookie, &data, &name)); CloseArchive(handle); // Assert that the names are as expected. std::sort(names.begin(), names.end()); ASSERT_EQ(expected_names_sorted, names); } static void AssertIterationOrder(const std::string_view prefix, const std::string_view suffix, const std::vector<std::string>& expected_names_sorted) { ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); void* iteration_cookie; ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, prefix, suffix)); AssertIterationNames(iteration_cookie, expected_names_sorted); CloseArchive(handle); } static void AssertIterationOrderWithMatcher(std::function<bool(std::string_view)> matcher, const std::vector<std::string>& expected_names_sorted) { ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); void* iteration_cookie; ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, matcher)); AssertIterationNames(iteration_cookie, expected_names_sorted); CloseArchive(handle); } TEST(ziparchive, Iteration) { static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/", "b/c.txt", "b/d.txt"}; Loading @@ -279,6 +291,30 @@ TEST(ziparchive, IterationWithPrefixAndSuffix) { AssertIterationOrder("b", ".txt", kExpectedMatchesSorted); } TEST(ziparchive, IterationWithAdditionalMatchesExactly) { static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt"}; auto matcher = [](std::string_view name) { return name == "a.txt"; }; AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted); } TEST(ziparchive, IterationWithAdditionalMatchesWithSuffix) { static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt", "b/d.txt"}; auto matcher = [](std::string_view name) { return name == "a.txt" || android::base::EndsWith(name, ".txt"); }; AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted); } TEST(ziparchive, IterationWithAdditionalMatchesWithPrefixAndSuffix) { static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b/c.txt", "b/d.txt"}; auto matcher = [](std::string_view name) { return name == "a.txt" || (android::base::EndsWith(name, ".txt") && android::base::StartsWith(name, "b/")); }; AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted); } TEST(ziparchive, IterationWithBadPrefixAndSuffix) { ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); Loading Loading
libziparchive/include/ziparchive/zip_archive.h +9 −0 Original line number Diff line number Diff line Loading @@ -187,6 +187,15 @@ int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr, const std::string_view optional_prefix = "", const std::string_view optional_suffix = ""); /* * Start iterating over all entries of a zip file. Use the matcher functor to * restrict iteration to entry names that make the functor return true. * * Returns 0 on success and negative values on failure. */ int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr, std::function<bool(std::string_view entry_name)> matcher); /* * Advance to the next element in the zipfile in iteration order. * Loading
libziparchive/zip_archive.cc +21 −12 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ #include <unistd.h> #include <memory> #include <optional> #include <vector> #if defined(__APPLE__) Loading Loading @@ -675,31 +676,40 @@ static int32_t FindEntry(const ZipArchive* archive, std::string_view entryName, struct IterationHandle { ZipArchive* archive; std::string prefix; std::string suffix; std::function<bool(std::string_view)> matcher; uint32_t position = 0; IterationHandle(ZipArchive* archive, std::string_view in_prefix, std::string_view in_suffix) : archive(archive), prefix(in_prefix), suffix(in_suffix) {} IterationHandle(ZipArchive* archive, std::function<bool(std::string_view)> in_matcher) : archive(archive), matcher(std::move(in_matcher)) {} bool Match(std::string_view entry_name) const { return matcher(entry_name); } }; int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr, const std::string_view optional_prefix, const std::string_view optional_suffix) { if (archive == nullptr || archive->cd_entry_map == nullptr) { ALOGW("Zip: Invalid ZipArchiveHandle"); return kInvalidHandle; } if (optional_prefix.size() > static_cast<size_t>(UINT16_MAX) || optional_suffix.size() > static_cast<size_t>(UINT16_MAX)) { ALOGW("Zip: prefix/suffix too long"); return kInvalidEntryName; } auto matcher = [prefix = std::string(optional_prefix), suffix = std::string(optional_suffix)](std::string_view name) mutable { return android::base::StartsWith(name, prefix) && android::base::EndsWith(name, suffix); }; return StartIteration(archive, cookie_ptr, std::move(matcher)); } int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr, std::function<bool(std::string_view)> matcher) { if (archive == nullptr || archive->cd_entry_map == nullptr) { ALOGW("Zip: Invalid ZipArchiveHandle"); return kInvalidHandle; } archive->cd_entry_map->ResetIteration(); *cookie_ptr = new IterationHandle(archive, optional_prefix, optional_suffix); *cookie_ptr = new IterationHandle(archive, matcher); return 0; } Loading Loading @@ -749,8 +759,7 @@ int32_t Next(void* cookie, ZipEntry* data, std::string_view* name) { auto entry = archive->cd_entry_map->Next(archive->central_directory.GetBasePtr()); while (entry != std::pair<std::string_view, uint64_t>()) { const auto [entry_name, offset] = entry; if (android::base::StartsWith(entry_name, handle->prefix) && android::base::EndsWith(entry_name, handle->suffix)) { if (handle->Match(entry_name)) { const int error = FindEntry(archive, entry_name, offset, data); if (!error && name) { *name = entry_name; Loading
libziparchive/zip_archive_test.cc +47 −11 Original line number Diff line number Diff line Loading @@ -227,32 +227,44 @@ TEST(ziparchive, Iteration_std_string_view) { CloseArchive(handle); } static void AssertIterationOrder(const std::string_view prefix, const std::string_view suffix, static void AssertIterationNames(void* iteration_cookie, const std::vector<std::string>& expected_names_sorted) { ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); void* iteration_cookie; ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, prefix, suffix)); ZipEntry data; std::vector<std::string> names; std::string name; for (size_t i = 0; i < expected_names_sorted.size(); ++i) { ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); names.push_back(name); } // End of iteration. ASSERT_EQ(-1, Next(iteration_cookie, &data, &name)); CloseArchive(handle); // Assert that the names are as expected. std::sort(names.begin(), names.end()); ASSERT_EQ(expected_names_sorted, names); } static void AssertIterationOrder(const std::string_view prefix, const std::string_view suffix, const std::vector<std::string>& expected_names_sorted) { ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); void* iteration_cookie; ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, prefix, suffix)); AssertIterationNames(iteration_cookie, expected_names_sorted); CloseArchive(handle); } static void AssertIterationOrderWithMatcher(std::function<bool(std::string_view)> matcher, const std::vector<std::string>& expected_names_sorted) { ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); void* iteration_cookie; ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, matcher)); AssertIterationNames(iteration_cookie, expected_names_sorted); CloseArchive(handle); } TEST(ziparchive, Iteration) { static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/", "b/c.txt", "b/d.txt"}; Loading @@ -279,6 +291,30 @@ TEST(ziparchive, IterationWithPrefixAndSuffix) { AssertIterationOrder("b", ".txt", kExpectedMatchesSorted); } TEST(ziparchive, IterationWithAdditionalMatchesExactly) { static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt"}; auto matcher = [](std::string_view name) { return name == "a.txt"; }; AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted); } TEST(ziparchive, IterationWithAdditionalMatchesWithSuffix) { static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt", "b/d.txt"}; auto matcher = [](std::string_view name) { return name == "a.txt" || android::base::EndsWith(name, ".txt"); }; AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted); } TEST(ziparchive, IterationWithAdditionalMatchesWithPrefixAndSuffix) { static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b/c.txt", "b/d.txt"}; auto matcher = [](std::string_view name) { return name == "a.txt" || (android::base::EndsWith(name, ".txt") && android::base::StartsWith(name, "b/")); }; AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted); } TEST(ziparchive, IterationWithBadPrefixAndSuffix) { ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); Loading