diff --git a/Userland/Applications/SpaceAnalyzer/Tree.cpp b/Userland/Applications/SpaceAnalyzer/Tree.cpp index 61b36cd664..bfc7909a64 100644 --- a/Userland/Applications/SpaceAnalyzer/Tree.cpp +++ b/Userland/Applications/SpaceAnalyzer/Tree.cpp @@ -5,7 +5,31 @@ */ #include "Tree.h" +#include +#include +#include #include +#include + +#include +#include +#include + +static constexpr size_t FILES_ENCOUNTERED_UPDATE_STEP_SIZE = 25; + +long long int TreeNode::update_totals() +{ + long long int result = 0; + if (m_children) { + for (auto& child : *m_children) { + result += child.update_totals(); + } + m_area = result; + } else { + result = m_area; + } + return result; +} void TreeNode::sort_children_by_area() const { @@ -14,3 +38,93 @@ void TreeNode::sort_children_by_area() const quick_sort(*children, [](auto& a, auto& b) { return b.m_area < a.m_area; }); } } + +struct QueueEntry { + QueueEntry(DeprecatedString path, TreeNode* node) + : path(move(path)) + , node(node) {}; + DeprecatedString path; + TreeNode* node { nullptr }; +}; + +static MountInfo* find_mount_for_path(DeprecatedString path, Vector& mounts) +{ + MountInfo* result = nullptr; + size_t length = 0; + for (auto& mount_info : mounts) { + DeprecatedString& mount_point = mount_info.mount_point; + if (path.starts_with(mount_point)) { + if (!result || mount_point.length() > length) { + result = &mount_info; + length = mount_point.length(); + } + } + } + return result; +} + +HashMap TreeNode::populate_filesize_tree(Vector& mounts, Function on_progress) +{ + VERIFY(!m_name.ends_with('/')); + + Queue queue; + queue.enqueue(QueueEntry(m_name, this)); + size_t files_encountered_count = 0; + HashMap error_accumulator; + + StringBuilder builder = StringBuilder(); + builder.append(m_name); + builder.append('/'); + MountInfo* root_mount_info = find_mount_for_path(builder.to_deprecated_string(), mounts); + if (!root_mount_info) { + return error_accumulator; + } + while (!queue.is_empty()) { + QueueEntry queue_entry = queue.dequeue(); + + builder.clear(); + builder.append(queue_entry.path); + builder.append('/'); + + MountInfo* mount_info = find_mount_for_path(builder.to_deprecated_string(), mounts); + if (!mount_info || (mount_info != root_mount_info && mount_info->source != root_mount_info->source)) { + continue; + } + + Core::DirIterator dir_iterator(builder.to_deprecated_string(), Core::DirIterator::SkipParentAndBaseDir); + if (dir_iterator.has_error()) { + int error_sum = error_accumulator.get(dir_iterator.error()).value_or(0); + error_accumulator.set(dir_iterator.error(), error_sum + 1); + } else { + queue_entry.node->m_children = make>(); + while (dir_iterator.has_next()) { + queue_entry.node->m_children->append(TreeNode(dir_iterator.next_path())); + } + for (auto& child : *queue_entry.node->m_children) { + files_encountered_count += 1; + if (!(files_encountered_count % FILES_ENCOUNTERED_UPDATE_STEP_SIZE)) + on_progress(files_encountered_count); + + DeprecatedString& name = child.m_name; + int name_len = name.length(); + builder.append(name); + struct stat st; + int stat_result = fstatat(dir_iterator.fd(), name.characters(), &st, AT_SYMLINK_NOFOLLOW); + if (stat_result < 0) { + int error_sum = error_accumulator.get(errno).value_or(0); + error_accumulator.set(errno, error_sum + 1); + } else { + if (S_ISDIR(st.st_mode)) { + queue.enqueue(QueueEntry(builder.to_deprecated_string(), &child)); + } else { + child.m_area = st.st_size; + } + } + builder.trim(name_len); + } + } + } + + update_totals(); + return error_accumulator; +} diff --git a/Userland/Applications/SpaceAnalyzer/Tree.h b/Userland/Applications/SpaceAnalyzer/Tree.h index f5784dfee2..ee63facaed 100644 --- a/Userland/Applications/SpaceAnalyzer/Tree.h +++ b/Userland/Applications/SpaceAnalyzer/Tree.h @@ -12,7 +12,13 @@ #include #include -struct TreeNode final { +struct MountInfo { + DeprecatedString mount_point; + DeprecatedString source; +}; + +class TreeNode final { +public: TreeNode(DeprecatedString name) : m_name(move(name)) {}; @@ -27,19 +33,26 @@ struct TreeNode final { } TreeNode const& child_at(size_t i) const { return m_children->at(i); } void sort_children_by_area() const; + HashMap populate_filesize_tree(Vector& mounts, Function on_progress); + +private: + long long int update_totals(); DeprecatedString m_name; i64 m_area { 0 }; OwnPtr> m_children; }; -struct Tree : public RefCounted { +class Tree : public RefCounted { +public: Tree(DeprecatedString root_name) : m_root(move(root_name)) {}; ~Tree() {}; - TreeNode m_root; - TreeNode const& root() const + TreeNode& root() { return m_root; }; + +private: + TreeNode m_root; }; diff --git a/Userland/Applications/SpaceAnalyzer/main.cpp b/Userland/Applications/SpaceAnalyzer/main.cpp index d234c9bc13..b36e5b6a68 100644 --- a/Userland/Applications/SpaceAnalyzer/main.cpp +++ b/Userland/Applications/SpaceAnalyzer/main.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -32,17 +31,9 @@ #include #include #include -#include -#include #include static constexpr auto APP_NAME = "Space Analyzer"sv; -static constexpr size_t FILES_ENCOUNTERED_UPDATE_STEP_SIZE = 25; - -struct MountInfo { - DeprecatedString mount_point; - DeprecatedString source; -}; static ErrorOr fill_mounts(Vector& output) { @@ -64,36 +55,6 @@ static ErrorOr fill_mounts(Vector& output) return {}; } -static MountInfo* find_mount_for_path(DeprecatedString path, Vector& mounts) -{ - MountInfo* result = nullptr; - size_t length = 0; - for (auto& mount_info : mounts) { - DeprecatedString& mount_point = mount_info.mount_point; - if (path.starts_with(mount_point)) { - if (!result || mount_point.length() > length) { - result = &mount_info; - length = mount_point.length(); - } - } - } - return result; -} - -static long long int update_totals(TreeNode& node) -{ - long long int result = 0; - if (node.m_children) { - for (auto& child : *node.m_children) { - result += update_totals(child); - } - node.m_area = result; - } else { - result = node.m_area; - } - return result; -} - static NonnullRefPtr create_progress_window() { auto window = GUI::Window::construct(); @@ -126,78 +87,6 @@ static void update_progress_label(GUI::Label& progresslabel, size_t files_encoun Core::EventLoop::current().pump(Core::EventLoop::WaitMode::PollForEvents); } -struct QueueEntry { - QueueEntry(DeprecatedString path, TreeNode* node) - : path(move(path)) - , node(node) {}; - DeprecatedString path; - TreeNode* node { nullptr }; -}; - -static void populate_filesize_tree(TreeNode& root, Vector& mounts, HashMap& error_accumulator, GUI::Label& progresslabel) -{ - VERIFY(!root.m_name.ends_with('/')); - - Queue queue; - queue.enqueue(QueueEntry(root.m_name, &root)); - size_t files_encountered_count = 0; - - StringBuilder builder = StringBuilder(); - builder.append(root.m_name); - builder.append('/'); - MountInfo* root_mount_info = find_mount_for_path(builder.to_deprecated_string(), mounts); - if (!root_mount_info) { - return; - } - while (!queue.is_empty()) { - QueueEntry queue_entry = queue.dequeue(); - - builder.clear(); - builder.append(queue_entry.path); - builder.append('/'); - - MountInfo* mount_info = find_mount_for_path(builder.to_deprecated_string(), mounts); - if (!mount_info || (mount_info != root_mount_info && mount_info->source != root_mount_info->source)) { - continue; - } - - Core::DirIterator dir_iterator(builder.to_deprecated_string(), Core::DirIterator::SkipParentAndBaseDir); - if (dir_iterator.has_error()) { - int error_sum = error_accumulator.get(dir_iterator.error()).value_or(0); - error_accumulator.set(dir_iterator.error(), error_sum + 1); - } else { - queue_entry.node->m_children = make>(); - while (dir_iterator.has_next()) { - queue_entry.node->m_children->append(TreeNode(dir_iterator.next_path())); - } - for (auto& child : *queue_entry.node->m_children) { - files_encountered_count += 1; - if (!(files_encountered_count % FILES_ENCOUNTERED_UPDATE_STEP_SIZE)) - update_progress_label(progresslabel, files_encountered_count); - - DeprecatedString& name = child.m_name; - int name_len = name.length(); - builder.append(name); - struct stat st; - int stat_result = fstatat(dir_iterator.fd(), name.characters(), &st, AT_SYMLINK_NOFOLLOW); - if (stat_result < 0) { - int error_sum = error_accumulator.get(errno).value_or(0); - error_accumulator.set(errno, error_sum + 1); - } else { - if (S_ISDIR(st.st_mode)) { - queue.enqueue(QueueEntry(builder.to_deprecated_string(), &child)); - } else { - child.m_area = st.st_size; - } - } - builder.trim(name_len); - } - } - } - - update_totals(root); -} - static ErrorOr analyze(RefPtr tree, SpaceAnalyzer::TreeMapWidget& treemapwidget, GUI::Statusbar& statusbar) { statusbar.set_text(""); @@ -209,27 +98,27 @@ static ErrorOr analyze(RefPtr tree, SpaceAnalyzer::TreeMapWidget& tr // Build an in-memory tree mirroring the filesystem and for each node // calculate the sum of the file size for all its descendants. - TreeNode* root = &tree->m_root; Vector mounts; TRY(fill_mounts(mounts)); - HashMap error_accumulator; - populate_filesize_tree(*root, mounts, error_accumulator, progresslabel); + auto errors = tree->root().populate_filesize_tree(mounts, [&](size_t processed_file_count) { + update_progress_label(progresslabel, processed_file_count); + }); progress_window->close(); // Display an error summary in the statusbar. - if (!error_accumulator.is_empty()) { + if (!errors.is_empty()) { StringBuilder builder; bool first = true; builder.append("Some directories were not analyzed: "sv); - for (auto& key : error_accumulator.keys()) { + for (auto& key : errors.keys()) { if (!first) { builder.append(", "sv); } auto const* error = strerror(key); builder.append({ error, strlen(error) }); builder.append(" ("sv); - int value = error_accumulator.get(key).value(); + int value = errors.get(key).value(); builder.append(DeprecatedString::number(value)); if (value == 1) { builder.append(" time"sv);