sapling/eden/fs/service/GlobNode.cpp
Chad Austin d0823ab865 unbox Dir Entries
Summary:
Dir's contents were represented as a vector of 64-bit
pointers to 48-byte structs.  This change removes that layer of
indirection, reducing memory usage and slightly pessimizing insertion.

The diff is mostly mechanical outside of the TreeInode.h changes and
calls to emplace..

I'll run memory tests tomorrow, though it's a gamble as to whether
private bytes will show a difference.  I may need to shrink the Entry
struct too.

Reviewed By: wez

Differential Revision: D6804957

fbshipit-source-id: b126656dbc7951565e74b6401adde6353e809056
2018-01-30 15:11:55 -08:00

239 lines
7.1 KiB
C++

/*
* Copyright (c) 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "GlobNode.h"
#include "EdenError.h"
#include "eden/fs/inodes/TreeInode.h"
using folly::Future;
using folly::makeFuture;
using folly::StringPiece;
using std::make_unique;
using std::string;
using std::unique_ptr;
using std::unordered_set;
using std::vector;
namespace facebook {
namespace eden {
GlobNode::GlobNode(StringPiece pattern, bool hasSpecials)
: pattern_(pattern), hasSpecials_(hasSpecials) {
if (pattern_ == "**" || pattern_ == "*") {
alwaysMatch_ = true;
} else {
auto compiled = GlobMatcher::create(pattern);
if (compiled.hasError()) {
throw newEdenError(
EINVAL,
"failed to compile pattern `{}` to GlobMatcher: {}",
pattern,
compiled.error());
}
matcher_ = std::move(compiled.value());
}
}
void GlobNode::parse(StringPiece pattern) {
GlobNode* parent = this;
while (!pattern.empty()) {
StringPiece token;
auto* container = &parent->children_;
bool hasSpecials;
if (pattern.startsWith("**")) {
// Recursive match defeats most optimizations; we have to stop
// tokenizing here.
token = pattern;
pattern = StringPiece();
container = &parent->recursiveChildren_;
hasSpecials = true;
} else {
token = tokenize(pattern, &hasSpecials);
}
auto node = lookupToken(container, token);
if (!node) {
container->emplace_back(std::make_unique<GlobNode>(token, hasSpecials));
node = container->back().get();
}
// If there are no more tokens remaining then we have a leaf node
// that will emit results. Update the node to reflect this.
// Note that this may convert a pre-existing node from an earlier
// glob specification to a leaf node.
if (pattern.empty()) {
node->isLeaf_ = true;
}
// Continue parsing the remainder of the pattern using this
// (possibly new) node as the parent.
parent = node;
}
}
Future<unordered_set<RelativePath>> GlobNode::evaluate(
RelativePathPiece rootPath,
TreeInodePtr root) {
unordered_set<RelativePath> results =
evaluateRecursiveComponent(rootPath, root).get();
vector<std::pair<PathComponent, GlobNode*>> recurse;
{
auto contents = root->getContents().rlock();
for (auto& node : children_) {
if (!node->hasSpecials_) {
// We can try a lookup for the exact name
auto it = contents->entries.find(PathComponentPiece(node->pattern_));
if (it != contents->entries.end()) {
// Matched!
if (node->isLeaf_) {
results.emplace((rootPath + it->first));
continue;
}
// Not the leaf of a pattern; if this is a dir, we need to recurse
if (it->second.isDirectory()) {
recurse.emplace_back(std::make_pair(it->first, node.get()));
}
}
} else {
// We need to match it out of the entries in this inode
for (auto& entry : contents->entries) {
if (node->alwaysMatch_ ||
node->matcher_.match(entry.first.stringPiece())) {
if (node->isLeaf_) {
results.emplace((rootPath + entry.first));
continue;
}
// Not the leaf of a pattern; if this is a dir, we need to
// recurse
if (entry.second.isDirectory()) {
recurse.emplace_back(std::make_pair(entry.first, node.get()));
}
}
}
}
}
}
// Recursively load child inodes and evaluate matches
std::vector<Future<unordered_set<RelativePath>>> futures;
for (auto& item : recurse) {
auto candidateName = rootPath + item.first;
futures.emplace_back(
root->getOrLoadChildTree(item.first)
.then([candidateName, node = item.second](TreeInodePtr dir) {
return node->evaluate(candidateName, dir);
}));
}
return folly::collect(futures).then(
[results = std::move(results)](
std::vector<std::unordered_set<RelativePath>>&& matchVector) mutable {
for (auto& matches : matchVector) {
results.insert(matches.begin(), matches.end());
}
return results;
});
}
StringPiece GlobNode::tokenize(StringPiece& pattern, bool* hasSpecials) {
*hasSpecials = false;
for (auto it = pattern.begin(); it != pattern.end(); ++it) {
switch (*it) {
case '*':
case '?':
case '[':
case '\\':
*hasSpecials = true;
break;
case '/':
// token is the input up-to-but-not-including the current position,
// which is a '/' character
StringPiece token(pattern.begin(), it);
// update the pattern to be the text after the slash
pattern = StringPiece(it + 1, pattern.end());
return token;
}
}
// No slash found, so the the rest of the pattern is the token
StringPiece token = pattern;
pattern = StringPiece();
return token;
}
GlobNode* GlobNode::lookupToken(
vector<unique_ptr<GlobNode>>* container,
StringPiece token) {
for (auto& child : *container) {
if (child->pattern_ == token) {
return child.get();
}
}
return nullptr;
}
Future<unordered_set<RelativePath>> GlobNode::evaluateRecursiveComponent(
RelativePathPiece rootPath,
TreeInodePtr root) {
unordered_set<RelativePath> results;
if (recursiveChildren_.empty()) {
return results;
}
vector<RelativePath> subDirNames;
{
auto contents = root->getContents().rlock();
for (auto& entry : contents->entries) {
auto candidateName = rootPath + entry.first;
for (auto& node : recursiveChildren_) {
if (node->alwaysMatch_ ||
node->matcher_.match(candidateName.stringPiece())) {
results.emplace(candidateName);
// No sense running multiple matches for this same file.
break;
}
}
// Remember to recurse through child dirs after we've released
// the lock on the contents.
if (entry.second.isDirectory()) {
subDirNames.emplace_back(candidateName);
}
}
}
// Recursively load child inodes and evaluate matches
std::vector<Future<unordered_set<RelativePath>>> futures;
for (auto& candidateName : subDirNames) {
futures.emplace_back(root->getOrLoadChildTree(candidateName.basename())
.then([candidateName, this](TreeInodePtr dir) {
return evaluateRecursiveComponent(
candidateName, dir);
}));
}
return folly::collect(futures).then(
[results = std::move(results)](
std::vector<std::unordered_set<RelativePath>>&& matchVector) mutable {
for (auto& matches : matchVector) {
results.insert(matches.begin(), matches.end());
}
return results;
});
}
} // namespace eden
} // namespace facebook