From 8d53442187af923bb92c256af7737a9a2d680c7c Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 27 Jul 2023 14:21:12 +0100 Subject: [PATCH] LibArchive: Extract logic for calculating ZIP statistics --- Userland/Libraries/LibArchive/Statistics.h | 33 ++++++++++++++++++++++ Userland/Libraries/LibArchive/Zip.cpp | 20 ++++++++++++- Userland/Libraries/LibArchive/Zip.h | 4 ++- Userland/Utilities/file.cpp | 22 ++++----------- Userland/Utilities/unzip.cpp | 9 ++---- 5 files changed, 63 insertions(+), 25 deletions(-) create mode 100644 Userland/Libraries/LibArchive/Statistics.h diff --git a/Userland/Libraries/LibArchive/Statistics.h b/Userland/Libraries/LibArchive/Statistics.h new file mode 100644 index 00000000000..bbd41d9dcb1 --- /dev/null +++ b/Userland/Libraries/LibArchive/Statistics.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Archive { + +class Statistics { +public: + Statistics(size_t file_count, size_t directory_count, size_t total_uncompressed_bytes) + : m_file_count(file_count) + , m_directory_count(directory_count) + , m_total_uncompressed_bytes(total_uncompressed_bytes) + { + } + + size_t file_count() const { return m_file_count; } + size_t directory_count() const { return m_directory_count; } + size_t member_count() const { return file_count() + directory_count(); } + size_t total_uncompressed_bytes() const { return m_total_uncompressed_bytes; } + +private: + size_t m_file_count { 0 }; + size_t m_directory_count { 0 }; + size_t m_total_uncompressed_bytes { 0 }; +}; + +} diff --git a/Userland/Libraries/LibArchive/Zip.cpp b/Userland/Libraries/LibArchive/Zip.cpp index a9904f4178d..e58d734becd 100644 --- a/Userland/Libraries/LibArchive/Zip.cpp +++ b/Userland/Libraries/LibArchive/Zip.cpp @@ -77,7 +77,7 @@ Optional Zip::try_create(ReadonlyBytes buffer) }; } -ErrorOr Zip::for_each_member(Function(ZipMember const&)> callback) +ErrorOr Zip::for_each_member(Function(ZipMember const&)> callback) const { size_t member_offset = m_members_start_offset; for (size_t i = 0; i < m_member_count; i++) { @@ -104,6 +104,24 @@ ErrorOr Zip::for_each_member(Function(ZipMember return true; } +ErrorOr Zip::calculate_statistics() const +{ + size_t file_count = 0; + size_t directory_count = 0; + size_t uncompressed_bytes = 0; + + TRY(for_each_member([&](auto zip_member) -> ErrorOr { + if (zip_member.is_directory) + directory_count++; + else + file_count++; + uncompressed_bytes += zip_member.uncompressed_size; + return IterationDecision::Continue; + })); + + return Statistics(file_count, directory_count, uncompressed_bytes); +} + ZipOutputStream::ZipOutputStream(NonnullOwnPtr stream) : m_stream(move(stream)) { diff --git a/Userland/Libraries/LibArchive/Zip.h b/Userland/Libraries/LibArchive/Zip.h index 9a1e940369d..cb9dbf9c3a8 100644 --- a/Userland/Libraries/LibArchive/Zip.h +++ b/Userland/Libraries/LibArchive/Zip.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -254,7 +255,8 @@ struct ZipMember { class Zip { public: static Optional try_create(ReadonlyBytes buffer); - ErrorOr for_each_member(Function(ZipMember const&)>); + ErrorOr for_each_member(Function(ZipMember const&)>) const; + ErrorOr calculate_statistics() const; private: static bool find_end_of_central_directory_offset(ReadonlyBytes, size_t& offset); diff --git a/Userland/Utilities/file.cpp b/Userland/Utilities/file.cpp index dfc1c3b90dd..fe5480afdf6 100644 --- a/Userland/Utilities/file.cpp +++ b/Userland/Utilities/file.cpp @@ -110,24 +110,14 @@ static ErrorOr> zip_details(StringView description, StringView { auto mapped_file = TRY(Core::MappedFile::map(path)); auto zip_file = Archive::Zip::try_create(mapped_file->bytes()); - u32 files = 0; - u32 directories = 0; - u64 total_bytes = 0; - TRY(zip_file->for_each_member([&](auto zip_member) -> ErrorOr { - if (zip_member.is_directory) - directories++; - else - files++; - total_bytes += zip_member.uncompressed_size; - return IterationDecision::Continue; - })); + auto statistics = TRY(zip_file->calculate_statistics()); return TRY(String::formatted("{}, {} {}, {} {} totaling {} uncompressed", description, - directories, - directories == 1 ? "directory" : "directories", - files, - files == 1 ? "file" : "files", - AK::human_readable_size(total_bytes))); + statistics.directory_count(), + statistics.directory_count() == 1 ? "directory" : "directories", + statistics.file_count(), + statistics.file_count() == 1 ? "file" : "files", + AK::human_readable_size(statistics.total_uncompressed_bytes()))); } static ErrorOr> elf_details(StringView description, StringView path) diff --git a/Userland/Utilities/unzip.cpp b/Userland/Utilities/unzip.cpp index 3d6d8f9794f..49964413448 100644 --- a/Userland/Utilities/unzip.cpp +++ b/Userland/Utilities/unzip.cpp @@ -146,22 +146,17 @@ ErrorOr serenity_main(Main::Arguments arguments) if (list_files) { outln(" Length Date Time Name"); outln("--------- ---------- -------- ----"); - u32 members_count = 0; - u64 total_size = 0; TRY(zip_file->for_each_member([&](auto zip_member) -> ErrorOr { - members_count++; - auto time = time_from_packed_dos(zip_member.modification_date, zip_member.modification_time); auto time_str = TRY(Core::DateTime::from_timestamp(time.seconds_since_epoch()).to_string()); - total_size += zip_member.uncompressed_size; - outln("{:>9} {} {}", zip_member.uncompressed_size, time_str, zip_member.name); return IterationDecision::Continue; })); + auto statistics = TRY(zip_file->calculate_statistics()); outln("--------- ----"); - outln("{:>9} {} files", total_size, members_count); + outln("{:>9} {} files", statistics.total_uncompressed_bytes(), statistics.member_count()); return 0; }