forked from OSchip/llvm-project
[VFS] Add support for "no_push" to VFS recursive iterators.
The "regular" file system has a useful feature that makes it possible to stop recursing when using the recursive directory iterators. This functionality was missing for the VFS recursive iterator and this patch adds that. Differential revision: https://reviews.llvm.org/D53465 llvm-svn: 345793
This commit is contained in:
parent
e7c7934a11
commit
41fb951f87
|
@ -193,14 +193,22 @@ public:
|
|||
|
||||
class FileSystem;
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// Keeps state for the recursive_directory_iterator.
|
||||
struct RecDirIterState {
|
||||
std::stack<directory_iterator, std::vector<directory_iterator>> Stack;
|
||||
bool HasNoPushRequest = false;
|
||||
};
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
/// An input iterator over the recursive contents of a virtual path,
|
||||
/// similar to llvm::sys::fs::recursive_directory_iterator.
|
||||
class recursive_directory_iterator {
|
||||
using IterState =
|
||||
std::stack<directory_iterator, std::vector<directory_iterator>>;
|
||||
|
||||
FileSystem *FS;
|
||||
std::shared_ptr<IterState> State; // Input iterator semantics on copy.
|
||||
std::shared_ptr<detail::RecDirIterState>
|
||||
State; // Input iterator semantics on copy.
|
||||
|
||||
public:
|
||||
recursive_directory_iterator(FileSystem &FS, const Twine &Path,
|
||||
|
@ -212,8 +220,8 @@ public:
|
|||
/// Equivalent to operator++, with an error code.
|
||||
recursive_directory_iterator &increment(std::error_code &EC);
|
||||
|
||||
const directory_entry &operator*() const { return *State->top(); }
|
||||
const directory_entry *operator->() const { return &*State->top(); }
|
||||
const directory_entry &operator*() const { return *State->Stack.top(); }
|
||||
const directory_entry *operator->() const { return &*State->Stack.top(); }
|
||||
|
||||
bool operator==(const recursive_directory_iterator &Other) const {
|
||||
return State == Other.State; // identity
|
||||
|
@ -224,9 +232,12 @@ public:
|
|||
|
||||
/// Gets the current level. Starting path is at level 0.
|
||||
int level() const {
|
||||
assert(!State->empty() && "Cannot get level without any iteration state");
|
||||
return State->size() - 1;
|
||||
assert(!State->Stack.empty() &&
|
||||
"Cannot get level without any iteration state");
|
||||
return State->Stack.size() - 1;
|
||||
}
|
||||
|
||||
void no_push() { State->HasNoPushRequest = true; }
|
||||
};
|
||||
|
||||
/// The virtual file system interface.
|
||||
|
|
|
@ -2157,28 +2157,33 @@ vfs::recursive_directory_iterator::recursive_directory_iterator(
|
|||
: FS(&FS_) {
|
||||
directory_iterator I = FS->dir_begin(Path, EC);
|
||||
if (I != directory_iterator()) {
|
||||
State = std::make_shared<IterState>();
|
||||
State->push(I);
|
||||
State = std::make_shared<detail::RecDirIterState>();
|
||||
State->Stack.push(I);
|
||||
}
|
||||
}
|
||||
|
||||
vfs::recursive_directory_iterator &
|
||||
recursive_directory_iterator::increment(std::error_code &EC) {
|
||||
assert(FS && State && !State->empty() && "incrementing past end");
|
||||
assert(!State->top()->path().empty() && "non-canonical end iterator");
|
||||
assert(FS && State && !State->Stack.empty() && "incrementing past end");
|
||||
assert(!State->Stack.top()->path().empty() && "non-canonical end iterator");
|
||||
vfs::directory_iterator End;
|
||||
if (State->top()->type() == sys::fs::file_type::directory_file) {
|
||||
vfs::directory_iterator I = FS->dir_begin(State->top()->path(), EC);
|
||||
if (I != End) {
|
||||
State->push(I);
|
||||
return *this;
|
||||
|
||||
if (State->HasNoPushRequest)
|
||||
State->HasNoPushRequest = false;
|
||||
else {
|
||||
if (State->Stack.top()->type() == sys::fs::file_type::directory_file) {
|
||||
vfs::directory_iterator I = FS->dir_begin(State->Stack.top()->path(), EC);
|
||||
if (I != End) {
|
||||
State->Stack.push(I);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (!State->empty() && State->top().increment(EC) == End)
|
||||
State->pop();
|
||||
while (!State->Stack.empty() && State->Stack.top().increment(EC) == End)
|
||||
State->Stack.pop();
|
||||
|
||||
if (State->empty())
|
||||
if (State->Stack.empty())
|
||||
State.reset(); // end iterator
|
||||
|
||||
return *this;
|
||||
|
|
|
@ -478,6 +478,85 @@ TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) {
|
|||
EXPECT_EQ(1, Counts[3]); // d
|
||||
}
|
||||
|
||||
TEST(VirtualFileSystemTest, BasicRealFSRecursiveIterationNoPush) {
|
||||
ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
|
||||
|
||||
ScopedDir _a(TestDirectory + "/a");
|
||||
ScopedDir _ab(TestDirectory + "/a/b");
|
||||
ScopedDir _c(TestDirectory + "/c");
|
||||
ScopedDir _cd(TestDirectory + "/c/d");
|
||||
ScopedDir _e(TestDirectory + "/e");
|
||||
ScopedDir _ef(TestDirectory + "/e/f");
|
||||
ScopedDir _g(TestDirectory + "/g");
|
||||
|
||||
IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
|
||||
|
||||
// Test that calling no_push on entries without subdirectories has no effect.
|
||||
{
|
||||
std::error_code EC;
|
||||
auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
|
||||
ASSERT_FALSE(EC);
|
||||
|
||||
std::vector<std::string> Contents;
|
||||
for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
|
||||
I.increment(EC)) {
|
||||
Contents.push_back(I->path());
|
||||
char last = I->path().back();
|
||||
switch (last) {
|
||||
case 'b':
|
||||
case 'd':
|
||||
case 'f':
|
||||
case 'g':
|
||||
I.no_push();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(7U, Contents.size());
|
||||
}
|
||||
|
||||
// Test that calling no_push skips subdirectories.
|
||||
{
|
||||
std::error_code EC;
|
||||
auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
|
||||
ASSERT_FALSE(EC);
|
||||
|
||||
std::vector<std::string> Contents;
|
||||
for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
|
||||
I.increment(EC)) {
|
||||
Contents.push_back(I->path());
|
||||
char last = I->path().back();
|
||||
switch (last) {
|
||||
case 'a':
|
||||
case 'c':
|
||||
case 'e':
|
||||
I.no_push();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check contents, which may be in any order
|
||||
EXPECT_EQ(4U, Contents.size());
|
||||
int Counts[7] = {0, 0, 0, 0, 0, 0, 0};
|
||||
for (const std::string &Name : Contents) {
|
||||
ASSERT_FALSE(Name.empty());
|
||||
int Index = Name[Name.size() - 1] - 'a';
|
||||
ASSERT_TRUE(Index >= 0 && Index < 7);
|
||||
Counts[Index]++;
|
||||
}
|
||||
EXPECT_EQ(1, Counts[0]); // a
|
||||
EXPECT_EQ(0, Counts[1]); // b
|
||||
EXPECT_EQ(1, Counts[2]); // c
|
||||
EXPECT_EQ(0, Counts[3]); // d
|
||||
EXPECT_EQ(1, Counts[4]); // e
|
||||
EXPECT_EQ(0, Counts[5]); // f
|
||||
EXPECT_EQ(1, Counts[6]); // g
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LLVM_ON_UNIX
|
||||
TEST(VirtualFileSystemTest, BrokenSymlinkRealFSRecursiveIteration) {
|
||||
ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
|
||||
|
|
Loading…
Reference in New Issue