mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 16:57:49 +03:00
d0823ab865
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
239 lines
7.1 KiB
C++
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
|