diff --git a/repos/gems/src/app/depot_query/main.cc b/repos/gems/src/app/depot_query/main.cc index be9b554537..dd722eeb16 100644 --- a/repos/gems/src/app/depot_query/main.cc +++ b/repos/gems/src/app/depot_query/main.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include /* fs_query includes */ @@ -28,12 +29,93 @@ namespace Depot_query { typedef String<64> Rom_label; + struct Directory_cache; struct Recursion_limit; struct Dependencies; struct Main; } +struct Depot_query::Directory_cache : Noncopyable +{ + Allocator &_alloc; + + using Name = Directory::Entry::Name; + + struct Listing; + using Listings = Dictionary; + + struct Listing : Listings::Element + { + Allocator &_alloc; + + struct File; + using Files = Dictionary; + + struct File : Files::Element + { + File(Files &files, Name const &name) : Files::Element(files, name) { } + }; + + Files _files { }; + + Listing(Listings &listings, Allocator &alloc, Directory &dir, + Directory::Path const &path) + : + Listings::Element(listings, path), _alloc(alloc) + { + try { + Directory(dir, path).for_each_entry([&] (Directory::Entry const &entry) { + new (_alloc) File(_files, entry.name()); }); + } + catch (Directory::Nonexistent_directory) { + warning("directory '", path, "' does not exist"); + } + } + + ~Listing() + { + auto destroy_fn = [&] (File &f) { destroy(_alloc, &f); }; + + while (_files.with_any_element(destroy_fn)); + } + + bool file_exists(Name const &name) const { return _files.exists(name); } + }; + + Listings mutable _listings { }; + + Directory_cache(Allocator &alloc) : _alloc(alloc) { } + + ~Directory_cache() + { + auto destroy_fn = [&] (Listing &l) { destroy(_alloc, &l); }; + + while (_listings.with_any_element(destroy_fn)); + } + + bool file_exists(Directory &dir, Directory::Path const &path, Name const &name) const + { + bool listing_known = false; + + bool const result = + _listings.with_element(path, + [&] /* match */ (Listing const &listing) { + listing_known = true; + return listing.file_exists(name); + }, + [&] /* no_match */ { return false; }); + + if (listing_known) + return result; + + Listing &new_listing = *new (_alloc) Listing(_listings, _alloc, dir, path); + + return new_listing.file_exists(name); + } +}; + + class Depot_query::Recursion_limit : Noncopyable { public: @@ -165,6 +247,8 @@ struct Depot_query::Main Directory _depot_dir { _root, "depot" }; + Constructible _directory_cache { }; + Signal_handler
_config_handler { _env.ep(), *this, &Main::_handle_config }; @@ -194,9 +278,14 @@ struct Depot_query::Main Architecture _architecture { }; - bool _file_exists(Directory::Path const &path) + bool _file_exists(Directory::Path const &path, Rom_label const &file_name) { - return _depot_dir.file_exists(path); + if (!_directory_cache.constructed()) { + error("directory cache is unexpectedly not constructed"); + return false; + } + + return _directory_cache->file_exists(_depot_dir, path, file_name); } template @@ -253,6 +342,8 @@ struct Depot_query::Main Xml_node const config = _config.xml(); + _directory_cache.construct(_heap); + /* * Depending of the 'query' config attribute, we obtain the query * information from a separate ROM session (attribute value "rom") @@ -394,25 +485,25 @@ Depot_query::Main::_find_rom_in_pkg(Directory::Path const &pkg_path, case Archive::SRC: { Archive::Path const - rom_path(Archive::user(archive_path), "/bin/", - _architecture, "/", - Archive::name(archive_path), "/", - Archive::version(archive_path), "/", rom_label); + rom_path(Archive::user(archive_path), "/bin/", + _architecture, "/", + Archive::name(archive_path), "/", + Archive::version(archive_path)); - if (_file_exists(rom_path)) - result = rom_path; + if (_file_exists(rom_path, rom_label)) + result = Archive::Path(rom_path, "/", rom_label); } break; case Archive::RAW: { Archive::Path const - rom_path(Archive::user(archive_path), "/raw/", - Archive::name(archive_path), "/", - Archive::version(archive_path), "/", rom_label); + rom_path(Archive::user(archive_path), "/raw/", + Archive::name(archive_path), "/", + Archive::version(archive_path)); - if (_file_exists(rom_path)) - result = rom_path; + if (_file_exists(rom_path, rom_label)) + result = Archive::Path(rom_path, "/", rom_label); } break;