serenity/Userland/Applications/SpaceAnalyzer/Tree.cpp
Ali Mohammad Pur 5e1499d104 Everywhere: Rename {Deprecated => Byte}String
This commit un-deprecates DeprecatedString, and repurposes it as a byte
string.
As the null state has already been removed, there are no other
particularly hairy blockers in repurposing this type as a byte string
(what it _really_ is).

This commit is auto-generated:
  $ xs=$(ack -l \bDeprecatedString\b\|deprecated_string AK Userland \
    Meta Ports Ladybird Tests Kernel)
  $ perl -pie 's/\bDeprecatedString\b/ByteString/g;
    s/deprecated_string/byte_string/g' $xs
  $ clang-format --style=file -i \
    $(git diff --name-only | grep \.cpp\|\.h)
  $ gn format $(git ls-files '*.gn' '*.gni')
2023-12-17 18:25:10 +03:30

149 lines
4.7 KiB
C++

/*
* Copyright (c) 2022, the SerenityOS developers.
* Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "Tree.h"
#include <AK/Function.h>
#include <AK/HashMap.h>
#include <AK/Queue.h>
#include <AK/QuickSort.h>
#include <LibCore/Directory.h>
#include <fcntl.h>
#include <sys/stat.h>
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
{
if (m_children) {
Vector<TreeNode>* children = const_cast<Vector<TreeNode>*>(m_children.ptr());
quick_sort(*children, [](auto& a, auto& b) { return b.m_area < a.m_area; });
}
}
struct QueueEntry {
QueueEntry(ByteString path, TreeNode* node)
: path(move(path))
, node(node) {};
ByteString path;
TreeNode* node { nullptr };
};
static MountInfo* find_mount_for_path(ByteString path, Vector<MountInfo>& mounts)
{
MountInfo* result = nullptr;
size_t length = 0;
for (auto& mount_info : mounts) {
ByteString& 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<int, int> TreeNode::populate_filesize_tree(Vector<MountInfo>& mounts, Function<void(size_t)> on_progress)
{
VERIFY(!m_name.ends_with('/'));
Queue<QueueEntry> queue;
queue.enqueue(QueueEntry(m_name, this));
size_t files_encountered_count = 0;
HashMap<int, int> error_accumulator;
auto log_error = [&](Error& error) {
auto error_code = error.code();
int error_sum = error_accumulator.get(error_code).value_or(0);
error_accumulator.set(error_code, error_sum + 1);
};
StringBuilder builder = StringBuilder();
builder.append(m_name);
builder.append('/');
MountInfo* root_mount_info = find_mount_for_path(builder.to_byte_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_byte_string(), mounts);
if (!mount_info || (mount_info != root_mount_info && mount_info->source != root_mount_info->source)) {
continue;
}
auto directory_or_error = Core::Directory::create(builder.string_view(), Core::Directory::CreateDirectories::No);
if (directory_or_error.is_error()) {
log_error(directory_or_error.error());
} else {
auto directory = directory_or_error.release_value();
queue_entry.node->m_children = make<Vector<TreeNode>>();
auto iteration_result = Core::Directory::for_each_entry(builder.string_view(), Core::DirIterator::SkipParentAndBaseDir, [&](auto& entry, auto&) -> ErrorOr<IterationDecision> {
TRY(queue_entry.node->m_children->try_append(TreeNode(entry.name)));
return IterationDecision::Continue;
});
if (iteration_result.is_error())
log_error(iteration_result.error());
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);
builder.append(child.m_name);
auto st_or_error = directory.stat(child.m_name, AT_SYMLINK_NOFOLLOW);
if (st_or_error.is_error()) {
log_error(st_or_error.error());
} else {
auto st = st_or_error.release_value();
if (S_ISDIR(st.st_mode)) {
queue.enqueue(QueueEntry(builder.to_byte_string(), &child));
} else {
child.m_area = st.st_size;
}
}
builder.trim(child.m_name.length());
}
}
}
update_totals();
return error_accumulator;
}
Optional<TreeNode const&> TreeNode::child_with_name(ByteString name) const
{
for (auto& child : *m_children) {
if (child.name() == name)
return child;
}
return {};
}