mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 10:44:58 +00:00

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')
148 lines
4.7 KiB
C++
148 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 {};
|
|
}
|