sapling/eden/fs/utils/PathFuncs.h
Xavier Deguillard fe7cc8b0a9 pathfuncs: remove boost::filesystem exported dependency
Summary:
The PathFuncs.h header is included pretty much everywhere in EdenFS, and thus
its compile speed affects the entirety of EdenFS. Unfortunately, the
boost::filesystem dependency is pretty large and is thus slowing down the
compilation.

Since the few methods that rely on Boost are only used in one place, let's
instead inline the use of Boost in these places.

Reviewed By: chadaustin, fanzeyi

Differential Revision: D35058316

fbshipit-source-id: b80a39f1575acc0fa4b9e64eb5375b95b4b421da
2022-03-22 15:48:40 -07:00

2112 lines
63 KiB
C++

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#pragma once
#include "eden/fs/utils/Memory.h"
#include <boost/operators.hpp>
#include <boost/system/error_code.hpp>
#include <fmt/format.h>
#include <folly/Expected.h>
#include <folly/FBString.h>
#include <folly/FBVector.h>
#include <folly/Format.h>
#include <folly/String.h>
#include <folly/hash/Hash.h>
#include <folly/logging/xlog.h>
#include <iterator>
#include <optional>
#include <type_traits>
#include "eden/fs/utils/CaseSensitivity.h"
#include "eden/fs/utils/Utf8.h"
#ifdef _WIN32
#include <eden/fs/utils/StringConv.h>
#endif
namespace facebook {
namespace eden {
/** Given a path like "foo/bar/baz" returns "baz" */
folly::StringPiece basename(folly::StringPiece path);
/** Given a path like "foo/bar/baz" returns "foo/bar" */
folly::StringPiece dirname(folly::StringPiece path);
/** Path directory separator.
*
* We always use a forward slash. On Windows systems we will normalize
* path names to alway use forward slash separators instead of backslashes.
*
* (This is defined as an enum value since we just want a symbolic constant,
* and don't want an actual symbol emitted by the compiler.)
*/
enum : char { kDirSeparator = '/', kWinDirSeparator = '\\' };
constexpr folly::StringPiece kDirSeparatorStr{"/"};
namespace detail {
inline constexpr bool isDirSeparator(char c) {
return c == kDirSeparator || (folly::kIsWindows && c == kWinDirSeparator);
}
inline constexpr bool isDirSeparator(folly::StringPiece str) {
return str.size() == 1 && isDirSeparator(str[0]);
}
inline size_t findPathSeparator(folly::StringPiece str, size_t start = 0) {
auto index = str.find(kDirSeparator, start);
if (folly::kIsWindows) {
auto winIndex = str.find(kWinDirSeparator, start);
if (winIndex != folly::StringPiece::npos) {
if (index == folly::StringPiece::npos) {
return winIndex;
} else {
return std::min(index, winIndex);
}
}
}
return index;
}
inline size_t rfindPathSeparator(folly::StringPiece str) {
auto index = str.rfind(kDirSeparator);
if (folly::kIsWindows) {
auto winIndex = str.rfind(kWinDirSeparator);
if (winIndex != folly::StringPiece::npos) {
if (index == folly::StringPiece::npos) {
return winIndex;
} else {
return std::max(index, winIndex);
}
}
}
return index;
}
} // namespace detail
/**
* FUSE supports components up to 1024 (FUSE_NAME_MAX) by default. For
* compatibility with other filesystems, Eden will limit to 255.
* https://en.wikipedia.org/wiki/Comparison_of_file_systems
*/
enum : size_t { kMaxPathComponentLength = 255 };
/* Some helpers for working with path composition.
* Goals:
*
* 1. Be StringPiece friendly
* 2. Allow strong typing to help us work with the various
* units of a path.
* 3. To be able to produce a composed path string without
* worrying about counting or looking for slashes.
* 4. To be able to decompose a path into a directory or base.
*
* Non-goals:
*
* 1. We don't care about canonicalization or realpath(), since we don't
* connect most of our paths to the filesystem VFS.
*
* Concepts:
*
* We introduce 3 types, each with a stored and non-stored variation:
*
* PathComponent, PathComponentPiece: represent a name within
* a directory. It is illegal for a PathComponent(Piece)? to
* contain a directory separator character, to be empty, or to be
* a relative ("." or "..") component.
*
* RelativePath, RelativePathPiece: represent any number of
* PathComponent(Piece)?'s composed together. It is illegal
* for a RelativePath to begin or be composed with an AbsolutePath(Piece)?.
* A RelativePath may be empty.
*
* AbsolutePath, AbsolutePathPiece: represent an absolute
* path. An absolute path must begin with a '/' character, and may be
* composed with PathComponents and RelativePaths, but not with other
* AbsolutePaths.
*
* Values of each of these types are immutable.
*/
namespace detail {
/// A type to select the constructors that skip sanity checks
struct SkipPathSanityCheck {};
template <typename STR>
class PathComponentBase;
template <typename STR>
class RelativePathBase;
template <typename STR>
class AbsolutePathBase;
} // namespace detail
/**
* Thrown when a PathComponent cannot be sanity checked.
*/
class PathComponentValidationError : public std::domain_error {
public:
explicit PathComponentValidationError(const std::string& s)
: std::domain_error(s) {}
};
/**
* Thrown when a PathComponent contains a directory separator.
*/
class PathComponentContainsDirectorySeparator
: public PathComponentValidationError {
public:
explicit PathComponentContainsDirectorySeparator(const std::string& s)
: PathComponentValidationError(s) {}
};
/**
* Thrown when a PathComponent isn't valid utf-8.
*/
class PathComponentNotUtf8 : public PathComponentValidationError {
public:
explicit PathComponentNotUtf8(const std::string& s)
: PathComponentValidationError(s) {}
};
// Intentionally use folly::fbstring because Dir entries are keyed on
// PathComponent and the fact that folly::fbstring is 24 bytes and std::string
// is 32 bytes adds up.
using PathComponent = detail::PathComponentBase<folly::fbstring>;
using PathComponentPiece = detail::PathComponentBase<folly::StringPiece>;
using RelativePath = detail::RelativePathBase<std::string>;
using RelativePathPiece = detail::RelativePathBase<folly::StringPiece>;
using AbsolutePath = detail::AbsolutePathBase<std::string>;
using AbsolutePathPiece = detail::AbsolutePathBase<folly::StringPiece>;
namespace detail {
// Helper for equality testing, borrowed from
// folly::detail::ComparableAsStringPiece in folly/Range.h
template <typename A, typename B, typename Stored, typename Piece>
struct StoredOrPieceComparableAsStringPiece {
enum {
value =
(std::is_convertible<A, folly::StringPiece>::value &&
(std::is_same<B, Stored>::value || std::is_same<B, Piece>::value)) ||
(std::is_convertible<B, folly::StringPiece>::value &&
(std::is_same<A, Stored>::value || std::is_same<A, Piece>::value))
};
};
/** Comparison operators.
* This is unfortunately a little repetitive.
* We only want to define these operators for the Stored and Piece variations
* of the type, as we don't want to allow comparing a PathComponent against a
* RelativePath.
* This has to be broken out as a base class to avoid a compilation error
* about redefining the friend operators; that happens because we instantiate
* two flavors of the same template with the same comparison ops.
*/
template <
typename Stored, // eg: Foo<std::string>
typename Piece, // eg: Foo<StringPiece>
bool IsComposed>
struct PathOperators {
// Less-than
friend bool operator<(const Stored& a, const Stored& b) {
return Piece{a} < Piece{b};
}
friend bool operator<(const Piece& a, const Stored& b) {
return a < Piece{b};
}
friend bool operator<(const Stored& a, const Piece& b) {
return Piece{a} < b;
}
friend bool operator<(const Piece& a, const Piece& b) {
if constexpr (IsComposed && folly::kIsWindows) {
auto aComponents = a.components();
auto bComponents = b.components();
return std::lexicographical_compare(
aComponents.begin(),
aComponents.end(),
bComponents.begin(),
bComponents.end());
} else {
return a.stringPiece() < b.stringPiece();
}
}
// Equality
friend bool operator==(const Stored& a, const Stored& b) {
return Piece{a} == Piece{b};
}
friend bool operator==(const Piece& a, const Stored& b) {
return a == Piece{b};
}
friend bool operator==(const Stored& a, const Piece& b) {
return Piece{a} == b;
}
friend bool operator==(const Piece& a, const Piece& b) {
if constexpr (IsComposed && folly::kIsWindows) {
auto aComponents = a.components();
auto bComponents = b.components();
return std::equal(
aComponents.begin(),
aComponents.end(),
bComponents.begin(),
bComponents.end());
} else {
return a.stringPiece() == b.stringPiece();
}
}
// Equality and Inequality vs stringy looking values.
// We allow testing against anything that is convertible to StringPiece.
// This template gunk generates the code for testing the following
// combinations DRY style:
// (Stored, convertible-to-StringPiece)
// (convertible-to-StringPiece, Stored)
// (Piece, convertible-to-StringPiece)
// (convertible-to-StringPiece, Piece)
template <typename A, typename B>
friend typename std::enable_if<
StoredOrPieceComparableAsStringPiece<A, B, Stored, Piece>::value,
bool>::type
operator==(const A& a, const B& rhs) {
return folly::StringPiece(a) == folly::StringPiece(rhs);
}
template <typename A, typename B>
friend typename std::enable_if<
StoredOrPieceComparableAsStringPiece<A, B, Stored, Piece>::value,
bool>::type
operator!=(const A& a, const B& rhs) {
return folly::StringPiece(a) != folly::StringPiece(rhs);
}
};
/** Defines the common path methods.
*
* PathBase is inherited by our consumer-visible types.
* It is templated around 4 type parameters:
*
* 1. Storage defines the nature of the data storage. We only
* use either std::string or StringPiece.
* 2. SanityChecker defines a "Deleter" style type that is used
* to validate the input for the constructors that apply sanity
* checks.
* 3. Stored defines the ultimate type of the variation that manages
* its own storage. eg: PathComponentBase<std::string>. We need this
* type to define appropriate relational operators and methods.
* 4. Piece defines the ultimate type of the variation that has no
* storage of its own. eg: PathComponentBase<StringPiece>. Similar
* to Stored above, we need this for relational operators and methods.
*/
template <
typename Storage, // eg: std::string or StringPiece
typename SanityChecker, // "Deleter" style type for checks
typename Stored, // eg: Foo<std::string>
typename Piece // eg: Foo<StringPiece>
>
class PathBase :
// ordering operators for this type
public std::conditional<
std::is_same<Storage, Stored>::value,
boost::totally_ordered<Stored, Piece>,
boost::totally_ordered<Piece>>::type {
protected:
Storage path_;
public:
// These type aliases are useful in other templates to be able
// to determine the Piece and Stored counterparts for a parameter.
using piece_type = Piece;
using stored_type = Stored;
/** Default construct an empty value. */
constexpr PathBase() {}
/** Construct from an untyped string value.
* Applies sanity checks. */
constexpr explicit PathBase(folly::StringPiece src)
: path_(src.data(), src.size()) {
SanityChecker()(src);
}
#ifdef _WIN32
constexpr explicit PathBase(std::wstring_view src)
: path_(wideToMultibyteString<Storage>(src)) {
SanityChecker()(stringPiece());
}
#endif
/** Construct from an untyped string value.
* Skips sanity checks. */
constexpr explicit PathBase(folly::StringPiece src, SkipPathSanityCheck)
: path_(src.data(), src.size()) {}
/** Construct from a stored variation of this type.
* Skips sanity checks. */
explicit PathBase(const Stored& other)
: path_(other.stringPiece().data(), other.stringPiece().size()) {}
/** Construct from a non-stored variation of this type.
* Skips sanity checks. */
explicit PathBase(const Piece& other)
: path_(other.stringPiece().data(), other.stringPiece().size()) {}
/** Move construct from a Stored value.
* Skips sanity checks.
* The template gunk only enables this constructor if we are the
* Stored flavor of this type.
* */
template <
/* need to alias Storage as StorageAlias because we can't directly use
* the class template parameter in the is_same check below */
typename StorageAlias = Storage,
typename = typename std::enable_if<
std::is_same<StorageAlias, std::string>::value>::type>
explicit PathBase(Stored&& other) : path_(std::move(other.path_)) {}
/** Move construct from an std::string value.
* Applies sanity checks.
* The template gunk only enables this constructor if we are the
* Stored flavor of this type.
* */
template <
/* need to alias Storage as StorageAlias because we can't directly use
* the class template parameter in the is_same check below */
typename StorageAlias = Storage,
typename = typename std::enable_if<
std::is_same<StorageAlias, std::string>::value>::type>
explicit PathBase(std::string&& str) : path_(std::move(str)) {
SanityChecker()(path_);
}
/** Move construct from an std::string value.
* Skips sanity checks.
* The template gunk only enables this constructor if we are the
* Stored flavor of this type.
* */
template <
/* need to alias Storage as StorageAlias because we can't directly use
* the class template parameter in the is_same check below */
typename StorageAlias = Storage,
typename = typename std::enable_if<
std::is_same<StorageAlias, std::string>::value>::type>
explicit PathBase(std::string&& str, SkipPathSanityCheck)
: path_(std::move(str)) {}
/// Return the path as a StringPiece
folly::StringPiece stringPiece() const {
return folly::StringPiece{path_};
}
/// Return a stored copy of this path
Stored copy() const {
return Stored(stringPiece(), SkipPathSanityCheck());
}
/// Return a non-stored reference to this path
Piece piece() const {
return Piece(stringPiece(), SkipPathSanityCheck());
}
/// Implicit conversion to Piece
/* implicit */ operator Piece() const {
return piece();
}
explicit operator folly::StringPiece() const {
return stringPiece();
}
/// Return a reference to the underlying stored value
const Storage& value() const& {
return path_;
}
/**
* If we are an rvalue-reference, return a rvalue-reference to our value.
*
* This allows callers to extract the string we contain if they desire.
*/
Storage&& value() && {
return std::move(path_);
}
#ifdef _WIN32
std::wstring wide() const {
auto str = multibyteToWideString(stringPiece());
std::replace(str.begin(), str.end(), L'/', L'\\');
return str;
}
#endif
};
/// Asserts that val is a well formed path component
struct PathComponentSanityCheck {
constexpr void operator()(folly::StringPiece val) const {
for (auto c : val) {
if (isDirSeparator(c)) {
throw PathComponentContainsDirectorySeparator(folly::to<std::string>(
"attempt to construct a PathComponent from a string containing a "
"directory separator: ",
val));
}
if (c == '\0') {
throw PathComponentValidationError(folly::to<std::string>(
"attempt to construct a PathComponent from a string containing a "
"nul byte: ",
val));
}
}
switch (val.size()) {
case 0:
throw PathComponentValidationError(
"cannot have an empty PathComponent");
case 1:
if ('.' == val[0]) {
throw PathComponentValidationError("PathComponent must not be .");
}
break;
case 2:
if ('.' == val[0] && '.' == val[1]) {
throw PathComponentValidationError("PathComponent must not be ..");
}
break;
}
if (!isValidUtf8(val)) {
throw PathComponentNotUtf8(folly::to<std::string>(
"attempt to construct a PathComponent from non valid UTF8 data: ",
val));
}
}
};
/** Represents a name within a directory.
* It is illegal for a PathComponent to contain a directory
* separator character. */
template <typename Storage>
class PathComponentBase
: public PathBase<
Storage,
PathComponentSanityCheck,
PathComponent,
PathComponentPiece>,
public PathOperators<PathComponent, PathComponentPiece, false> {
public:
// Inherit constructors
using base_type = PathBase<
Storage,
PathComponentSanityCheck,
PathComponent,
PathComponentPiece>;
using base_type::base_type;
/// Forbid empty PathComponents
explicit PathComponentBase() = delete;
};
/**
* An iterator over prefixes of a composed path
*
* Iterating yields a series of composed path elements.
* For example, iterating the path "foo/bar/baz" will yield
* this series of Piece elements:
*
* 1. "/" but only for AbsolutePath
* 2. "foo"
* 3. "foo/bar"
* 4. "foo/bar/baz"
*
* You may use the dirname() and basename() methods to focus
* on the portions of interest.
*
* Note: ComposedPathIterator doesn't really meet all of the requirements of an
* InputIterator (operator*() returns a value rather than a reference, and
* operator->() isn't implemented). Nonetheless we are still using
* std::input_iterator_tag here, since there isn't a standard tag for iterators
* that meet the generic Iterator requirements (section 24.2.2 of the C++17
* standard) but not the InputIterator requirements (section 24.2.3). If we
* don't use input_iterator_tag this breaks other functionality, such as being
* able to use ComposedPathIterator as vector constructor arguments.
*/
template <typename Piece, bool IsReverse>
class ComposedPathIterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = const Piece;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
using position_type = folly::StringPiece::const_iterator;
explicit ComposedPathIterator() : path_(), pos_(nullptr) {}
ComposedPathIterator(const ComposedPathIterator& other) = default;
ComposedPathIterator& operator=(const ComposedPathIterator& other) = default;
/// Initialize the iterator and point to the start of the path.
explicit ComposedPathIterator(Piece path)
: path_(path.stringPiece()), pos_(pathBegin()) {}
/** Initialize the iterator at an arbitrary position. */
ComposedPathIterator(Piece path, position_type pos)
: path_(path.stringPiece()), pos_(pos) {}
bool operator==(const ComposedPathIterator& other) const {
XDCHECK_EQ(path_, other.path_);
return pos_ == other.pos_;
}
bool operator!=(const ComposedPathIterator& other) const {
XDCHECK_EQ(path_, other.path_);
return pos_ != other.pos_;
}
/// ++iter;
ComposedPathIterator& operator++() {
if (IsReverse) {
retreat();
} else {
advance();
}
return *this;
}
/// iter++;
ComposedPathIterator operator++(int) {
ComposedPathIterator tmp(*this);
++(*this); // invoke the ++iter handler above.
return tmp;
}
/// --iter;
ComposedPathIterator& operator--() {
if (IsReverse) {
advance();
} else {
retreat();
}
return *this;
}
/// iter--;
ComposedPathIterator operator--(int) {
ComposedPathIterator tmp(*this);
--(*this); // invoke the --iter handler above.
return tmp;
}
/// Returns the piece for the current iterator position.
Piece piece() const {
XCHECK_NE(pos_, nullptr);
// Return everything preceding the slash to which pos_ points.
return Piece(folly::StringPiece(path_.begin(), pos_));
}
/*
* Note: dereferencing a ComposedPathIterator returns a new
* ComposedPathPiece, and not a reference to an existing ComposedPathPiece.
*/
Piece operator*() const {
return piece();
}
/**
* Returns a RelativePathPiece that corresponds to the characters in the path
* "to the right of" the current iterator position. Note that this is true
* whether this is a forward or reverse iterator.
*/
RelativePathPiece remainder() const;
/*
* TODO: operator->() is not implemented
*
* Since the operator*() returns a new Piece and not a reference,
* operator->() can't really be implemented correctly, as it needs to return
* a pointer to some existing object.
*/
// Piece* operator->() const;
protected:
const char* pathBegin() {
if (std::is_same<Piece, AbsolutePathPiece>::value) {
// Always start iterators at the initial "/" character, so
// that begin() yields "/" instead of the empty string.
return path_.begin() + 1;
} else {
return path_.begin();
}
}
// Move the iterator forwards in the path.
void advance() {
if (IsReverse) {
if (pos_ == nullptr) {
pos_ = pathBegin();
return;
}
XCHECK_NE(pos_, path_.end());
} else {
XCHECK_NE(pos_, nullptr);
if (pos_ == path_.end()) {
pos_ = nullptr;
return;
}
}
++pos_;
while (pos_ < path_.end() && !isDirSeparator(*pos_)) {
++pos_;
}
}
// Move the iterator backwards in the path.
void retreat() {
auto stopPos = pathBegin();
if (IsReverse) {
XCHECK_NE(pos_, nullptr);
if (pos_ <= stopPos) {
pos_ = nullptr;
return;
}
} else {
if (pos_ == nullptr) {
pos_ = path_.end();
return;
}
XCHECK_NE(pos_, stopPos);
}
--pos_;
while (pos_ > stopPos && !isDirSeparator(*pos_)) {
--pos_;
}
}
/// the path we're iterating over.
folly::StringPiece path_;
/// our current position within that path.
position_type pos_;
};
/**
* An iterator over components in a composed path.
*/
template <bool IsReverse>
class PathComponentIterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = const PathComponentPiece;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
using position_type = folly::StringPiece::const_iterator;
enum EndEnum { END };
explicit PathComponentIterator() {}
PathComponentIterator(const PathComponentIterator& other) = default;
PathComponentIterator& operator=(const PathComponentIterator& other) =
default;
// Construct a PathComponentIterator from a composed path
template <typename ComposedPathType>
explicit PathComponentIterator(const ComposedPathType& path)
: path_{path.stringPiece()} {
if (IsReverse) {
start_ = path_.end();
end_ = path_.end();
// Back start_ up to just after the last kDirSeparator
while (start_ != path_.begin() && !isDirSeparator(*(start_ - 1))) {
--start_;
}
} else {
// Skip over any leading slash, to handle absolute paths
start_ = path_.begin();
while (start_ != path_.end() && isDirSeparator(*start_)) {
++start_;
}
// Advance end_ until the next slash or the end of the path
end_ = start_;
while (end_ != path_.end() && !isDirSeparator(*end_)) {
++end_;
}
}
}
template <typename ComposedPathType>
explicit PathComponentIterator(const ComposedPathType& path, EndEnum)
: path_{path.stringPiece()} {
if (IsReverse) {
start_ = path_.begin();
end_ = path_.begin();
} else {
start_ = path_.end();
end_ = path_.end();
}
}
bool operator==(const PathComponentIterator& other) const {
XDCHECK_EQ(path_, other.path_);
// We have to check both start_ and end_ here.
// In most cases start_ will equal other.start_ if and only if end_ equals
// other.end_. However, this is not always true because of end() and
// rend(). end_ points the same place at end() and end() - 1.
// start_ points to the same place at rend() and rend() - 1.
return (start_ == other.start_) && (end_ == other.end_);
}
bool operator!=(const PathComponentIterator& other) const {
XDCHECK_EQ(path_, other.path_);
return (start_ != other.start_) || (end_ != other.end_);
}
/// ++iter;
PathComponentIterator& operator++() {
if (IsReverse) {
retreat();
} else {
advance();
}
return *this;
}
/// iter++;
PathComponentIterator operator++(int) {
PathComponentIterator tmp(*this);
++(*this); // invoke the ++iter handler above.
return tmp;
}
/// --iter;
PathComponentIterator& operator--() {
if (IsReverse) {
advance();
} else {
retreat();
}
return *this;
}
/// iter--;
PathComponentIterator operator--(int) {
PathComponentIterator tmp(*this);
--(*this); // invoke the --iter handler above.
return tmp;
}
/// Returns the piece for the current iterator position.
PathComponentPiece piece() const {
return PathComponentPiece{
folly::StringPiece{start_, end_}, SkipPathSanityCheck{}};
}
/*
* Note: dereferencing a PathComponentIterator returns a new
* PathComponentPiece, and not a reference to an existing PathComponentPiece.
*/
PathComponentPiece operator*() const {
return piece();
}
/*
* TODO: operator->() is not implemented
*
* Since the operator*() returns a new Piece and not a reference,
* operator->() can't really be implemented correctly, as it needs to return
* a pointer to some existing object.
*/
// Piece* operator->() const;
private:
// Move the iterator forwards in the path.
void advance() {
XDCHECK_NE(start_, path_.end());
if (end_ == path_.end()) {
start_ = end_;
return;
}
++end_;
start_ = end_;
while (end_ != path_.end() && !isDirSeparator(*end_)) {
++end_;
}
}
// Move the iterator backwards in the path.
void retreat() {
XDCHECK_NE(end_, path_.begin());
if (start_ == path_.begin()) {
end_ = path_.begin();
return;
}
--start_;
end_ = start_;
while (start_ != path_.begin() && !isDirSeparator(*(start_ - 1))) {
--start_;
}
}
/// the path we're iterating over.
folly::StringPiece path_;
/// our current position within that path.
position_type start_{nullptr};
position_type end_{nullptr};
};
template <bool IsReverse>
class PathSuffixIterator;
/** A pair of path iterators.
* This is used to implement the paths() and allPaths() methods.
*/
template <typename Iterator>
class PathIteratorRange {
public:
using iterator = Iterator;
PathIteratorRange(iterator b, iterator e)
: begin_(std::move(b)), end_(std::move(e)) {}
iterator begin() const {
return begin_;
}
iterator end() const {
return end_;
}
private:
iterator begin_;
iterator end_;
};
/** Represents any number of PathComponents composed together.
* This is a base implementation that powers both RelativePath
* and AbsolutePath so that we can share the definition of the methods below.
* */
template <
typename Storage, // eg: std::string or StringPiece
typename SanityChecker, // "Deleter" style type for checks
typename Stored, // eg: Foo<std::string>
typename Piece // eg: Foo<StringPiece>
>
class ComposedPathBase : public PathBase<Storage, SanityChecker, Stored, Piece>,
public PathOperators<Stored, Piece, true> {
public:
// Inherit constructors
using base_type = PathBase<Storage, SanityChecker, Stored, Piece>;
using base_type::base_type;
// Component iterator types
using component_iterator = PathComponentIterator<false>;
using reverse_component_iterator = PathComponentIterator<true>;
using component_iterator_range = PathIteratorRange<component_iterator>;
using reverse_component_iterator_range =
PathIteratorRange<reverse_component_iterator>;
/// Return the final component of this path
PathComponentPiece basename() const {
return PathComponentPiece(
facebook::eden::basename(this->path_), SkipPathSanityCheck());
}
/** Return the dirname.
* That is a non-stored reference to everything except the final
* component of the path. */
Piece dirname() const {
return Piece(
facebook::eden::dirname(this->stringPiece()), SkipPathSanityCheck());
}
/** Return an iterator range that will yield all components of this path.
*
* For example, iterating the relative path "foo/bar/baz" will yield
* this series of PathComponentPiece elements:
*
* 1. "foo"
* 2. "bar"
* 3. "baz"
*
* Iterating the absolute path "/foo/bar/baz" would also yield the same
* sequence.
*/
component_iterator_range components() const {
auto p = this->piece();
return component_iterator_range(
component_iterator{p}, component_iterator{p, component_iterator::END});
}
/** Return an iterator range that will yield all components of this path in
* reverse.
*/
reverse_component_iterator_range rcomponents() const {
auto p = this->piece();
return reverse_component_iterator_range(
reverse_component_iterator{p},
reverse_component_iterator{p, reverse_component_iterator::END});
}
};
/// Asserts that val is formed of multiple well formed PathComponents.
struct ComposedPathSanityCheck {
constexpr size_t nextSeparator(folly::StringPiece val, size_t start) const {
const char* data = val.data();
for (size_t i = start; i < val.size(); i++) {
if (isDirSeparator(data[i])) {
return i;
}
}
return folly::StringPiece::npos;
}
constexpr void operator()(folly::StringPiece val) const {
size_t start = 0;
while (true) {
auto next = nextSeparator(val, start);
if (next == folly::StringPiece::npos) {
break;
}
PathComponentSanityCheck()(
folly::StringPiece{val.begin() + start, next - start});
start = next + 1;
}
// Last component
PathComponentSanityCheck()(
folly::StringPiece{val.begin() + start, val.end()});
}
};
/// Asserts that val is well formed relative path
struct RelativePathSanityCheck {
constexpr void operator()(folly::StringPiece val) const {
if (!val.empty()) {
const char* data = val.data();
if (isDirSeparator(data[0])) {
throw std::domain_error(folly::to<std::string>(
"attempt to construct a RelativePath from an absolute path string: ",
val));
}
if (isDirSeparator(data[val.size() - 1])) {
throw std::domain_error(folly::to<std::string>(
"RelativePath must not end with a slash: ", val));
}
ComposedPathSanityCheck()(val);
}
}
};
/** Represents any number of PathComponents composed together.
* It is illegal for a RelativePath to begin with an absolute
* path prefix (`/` on unix, more complex on windows, but we
* haven't implemented that yet in any case)
*
* A RelativePath may be the empty string.
*/
template <typename Storage>
class RelativePathBase : public ComposedPathBase<
Storage,
RelativePathSanityCheck,
RelativePath,
RelativePathPiece> {
public:
// Inherit constructors
using base_type = ComposedPathBase<
Storage,
RelativePathSanityCheck,
RelativePath,
RelativePathPiece>;
using base_type::base_type;
/** Construct from a PathComponent */
template <typename T>
explicit RelativePathBase(const PathComponentBase<T>& comp)
: base_type(comp.stringPiece(), SkipPathSanityCheck()) {}
/** Allow constructing empty */
RelativePathBase() {}
// For iteration
using iterator = ComposedPathIterator<RelativePathPiece, false>;
using reverse_iterator = ComposedPathIterator<RelativePathPiece, true>;
using iterator_range = PathIteratorRange<iterator>;
using reverse_iterator_range = PathIteratorRange<reverse_iterator>;
// Suffix iterator types
using suffix_iterator = PathSuffixIterator<false>;
using suffix_iterator_range = PathIteratorRange<suffix_iterator>;
using reverse_suffix_iterator = PathSuffixIterator<true>;
using reverse_suffix_iterator_range =
PathIteratorRange<reverse_suffix_iterator>;
/**
* Return an iterator range that will yield all parent directories of this
* path, and then the path itself.
*
* For example, iterating the path "foo/bar/baz" will yield
* this series of Piece elements:
*
* 1. "foo"
* 2. "foo/bar"
* 3. "foo/bar/baz"
*
* See also the suffixes() and rsuffixes() methods, which provide iterators
* over directory suffixes.
*/
iterator_range paths() const {
auto p = this->piece();
return iterator_range(++iterator{p}, iterator{p, nullptr});
}
/**
* Return an iterator range that will yield all parent directories of this
* path, and then the path itself.
*
* This is very similar to paths(), but also includes the empty string
* first, to represent the current directory that this path is relative to.
*
* For example, iterating the path "foo/bar/baz" will yield
* this series of Piece elements:
*
* 1. ""
* 2. "foo"
* 3. "foo/bar"
* 4. "foo/bar/baz"
*/
iterator_range allPaths() const {
auto p = this->piece();
return iterator_range(iterator{p}, iterator{p, nullptr});
}
/**
* Return a reverse_iterator over all parent directories and this path.
*
* See also the suffixes() and rsuffixes() methods, which provide iterators
* over directory suffixes.
*/
reverse_iterator_range rpaths() const {
auto p = this->piece();
return reverse_iterator_range(
reverse_iterator{p, this->stringPiece().end()},
reverse_iterator{p, this->stringPiece().begin()});
}
/** Return a reverse_iterator over this path and all parent directories,
* including the empty path at the end.
*/
reverse_iterator_range rallPaths() const {
auto p = this->piece();
return reverse_iterator_range(
reverse_iterator{p, this->stringPiece().end()},
reverse_iterator{p, nullptr});
}
/**
* Return an iterator range over all directory suffixes of the path.
*
* For example, iterating the path "foo/bar/baz" will yield
* this series of Piece elements:
*
* 1. "foo/bar/baz"
* 2. "bar/baz"
* 3. "baz"
*
* See also the paths() and rpaths() methods, which provide iterators over
* directory prefixes.
*/
suffix_iterator_range suffixes() const;
/**
* Return an iterator range over all directory suffixes of the path, from
* back to front.
*
* For example, iterating the path "foo/bar/baz" will yield
* this series of Piece elements:
*
* 1. "baz"
* 2. "bar/baz"
* 3. "foo/bar/baz"
*
* See also the paths() and rpaths() methods, which provide iterators over
* directory prefixes.
*/
reverse_suffix_iterator_range rsuffixes() const;
/** Return an iterator to the specified parent directory of this path.
* If parent is not a parent directory of this path, returns
* allPaths().end().
*
* The resulting iterator will fall within the range
* [allPaths.begin(), allPaths.end()]
* It can be incremented forwards or backwards until either end of this
* range.
*/
iterator findParent(const RelativePathPiece& parent) const {
auto parentPiece = parent.stringPiece();
if (this->path_.size() <= parentPiece.size()) {
return allPaths().end();
}
if (parentPiece.empty()) {
// Note: this returns an iterator to an empty path.
return allPaths().begin();
}
if (!isDirSeparator(this->path_[parentPiece.size()])) {
return allPaths().end();
}
folly::StringPiece prefix{this->path_.data(), parentPiece.size()};
if (prefix != parentPiece) {
return allPaths().end();
}
return iterator(
this->piece(), this->stringPiece().begin() + parentPiece.size());
}
/** Construct from an iterable set of PathComponents.
* This should match iterators with values from which we can construct
* PathComponentPiece.
* */
template <
typename Iterator,
typename = typename std::enable_if<std::is_constructible<
PathComponentPiece,
typename std::iterator_traits<Iterator>::reference>::value>::type>
RelativePathBase(Iterator begin, Iterator end) {
folly::fbvector<folly::StringPiece> components;
while (begin != end) {
components.emplace_back(PathComponentPiece{*begin}.stringPiece());
++begin;
}
folly::join(kDirSeparatorStr, components, this->path_);
}
/** Construct from a container that holds PathComponents.
* This should match containers of values from which we can construct
* PathComponentPiece.
* */
template <
typename Container,
typename = typename std::enable_if<std::is_constructible<
PathComponentPiece,
typename Container::const_reference>::value>::type>
explicit RelativePathBase(const Container& container)
: RelativePathBase(container.cbegin(), container.cend()) {}
/** Construct from an initializer list of PathComponents. */
explicit RelativePathBase(
const std::initializer_list<PathComponentPiece>& values)
: RelativePathBase(values.begin(), values.end()) {}
/// Return true if this is an empty relative path
bool empty() const {
return this->path_.empty();
}
/** Return true if this path is a subdirectory of the specified path
* Returns false if the two paths refer to the exact same directory.
*/
bool isSubDirOf(const RelativePathPiece& other) const {
return this->findParent(other) != allPaths().end();
}
/** Return true if this path is a parent directory of the specified path
* Returns false if the two paths refer to the exact same directory.
*/
bool isParentDirOf(const RelativePathPiece& other) const {
return other.findParent(*this) != other.allPaths().end();
}
}; // namespace detail
/// Asserts that val is well formed absolute path
struct AbsolutePathSanityCheck {
void operator()(folly::StringPiece val) const {
#ifndef _WIN32
// This won't work on Windows. The usermode Windows path can start with
// a drive letter in front: c:\folder\file.txt
if (!val.startsWith(kDirSeparator)) {
throw std::domain_error(folly::to<std::string>(
"attempt to construct an AbsolutePath from a non-absolute string: \"",
val,
"\""));
}
#endif
if (val.size() > 1 && val.endsWith(kDirSeparator)) {
// We do allow "/" though
throw std::domain_error(folly::to<std::string>(
"AbsolutePath must not end with a slash: ", val));
}
if (val.size() > 1) {
ComposedPathSanityCheck()(folly::StringPiece{val.begin() + 1, val.end()});
}
}
};
/** An AbsolutePath must begin with an absolute path character.
*
* It can be produced either explicitly from a string (perhaps
* obtained via configuration), or by composing an AbsolutePath
* with a RelativePath or PathComponent.
*/
template <typename Storage>
class AbsolutePathBase : public ComposedPathBase<
Storage,
AbsolutePathSanityCheck,
AbsolutePath,
AbsolutePathPiece> {
public:
// Inherit constructors
using base_type = ComposedPathBase<
Storage,
AbsolutePathSanityCheck,
AbsolutePath,
AbsolutePathPiece>;
using base_type::base_type;
// Default construct to the root of the VFS
AbsolutePathBase() : base_type(kDirSeparatorStr, SkipPathSanityCheck()) {}
// For iteration
using iterator = ComposedPathIterator<AbsolutePathPiece, false>;
using reverse_iterator = ComposedPathIterator<AbsolutePathPiece, true>;
using iterator_range = PathIteratorRange<iterator>;
using reverse_iterator_range = PathIteratorRange<reverse_iterator>;
// Suffix iterator types
using suffix_iterator = PathSuffixIterator<false>;
using suffix_iterator_range = PathIteratorRange<suffix_iterator>;
using reverse_suffix_iterator = PathSuffixIterator<true>;
using reverse_suffix_iterator_range =
PathIteratorRange<reverse_suffix_iterator>;
iterator_range paths() const {
auto p = this->piece();
return iterator_range(iterator{p}, iterator{p, nullptr});
}
reverse_iterator_range rpaths() const {
auto p = this->piece();
return reverse_iterator_range(
reverse_iterator{p, this->stringPiece().end()},
reverse_iterator{p, nullptr});
}
/**
* Return an iterator range over all suffixes of the path.
*
* For example, iterating the path "/foo/bar/baz" will yield
* this series of Piece elements:
*
* 1. "foo/bar/baz"
* 2. "bar/baz"
* 3. "baz"
*/
suffix_iterator_range suffixes() const;
/**
* Return an iterator range over all suffixes of the path, from back to
* front.
*
* For example, iterating the path "/foo/bar/baz" will yield
* this series of Piece elements:
*
* 1. "baz"
* 2. "bar/baz"
* 3. "foo/bar/baz"
*/
reverse_suffix_iterator_range rsuffixes() const;
/**
* This must be equal to or an ancestor of the specified path.
* If `this` is "/foo" and `child` is "/foo/bar/baz", then this returns
* `"bar/baz"_relpath`. If `this` and `child` are equal, then this
* returns `RelativePathPiece()`.
*/
RelativePathPiece relativize(AbsolutePathPiece child) const {
auto myPaths = this->paths();
auto childPaths = child.paths();
auto myIter = myPaths.begin();
auto childIter = childPaths.begin();
while (true) {
if (childIter == childPaths.end()) {
throw std::runtime_error(folly::to<std::string>(
child, " should be under ", this->stringPiece()));
}
// Note that a RelativePath cannot contain "../" path elements.
if (myIter.piece() != childIter.piece()) {
throw std::runtime_error(folly::to<std::string>(
this->stringPiece(), " does not seem to be a prefix of ", child));
}
myIter++;
if (myIter != myPaths.end()) {
childIter++;
} else {
// We do not want to increment childIter here or else we will be
// missing a path component when we call remainder() after the while
// loop.
break;
}
}
return childIter.remainder();
}
/** Compose an AbsolutePath with a RelativePath */
template <typename B>
AbsolutePath operator+(const detail::RelativePathBase<B>& b) const {
// A RelativePath may be empty, in which case we simply return a copy
// of the absolute path.
if (b.stringPiece().empty()) {
return this->copy();
}
if (isDirSeparator(this->stringPiece())) {
// Special case to avoid building a string like "//foo"
return AbsolutePath(
folly::to<std::string>(this->stringPiece(), b.stringPiece()),
detail::SkipPathSanityCheck());
}
return AbsolutePath(
folly::to<std::string>(
this->stringPiece(), kDirSeparatorStr, b.stringPiece()),
detail::SkipPathSanityCheck());
}
/** Compose an AbsolutePath with a PathComponent */
template <typename B>
AbsolutePath operator+(const detail::PathComponentBase<B>& b) const {
return *this + RelativePathPiece(b);
}
/** Convert to a c-string for use in syscalls
* The template gunk only enables this constructor if we are the
* Stored flavor of this type.
* */
template <
/* need to alias Storage as StorageAlias because we can't directly use
* the class template parameter in the is_same check below */
typename StorageAlias = Storage,
typename = typename std::enable_if<
std::is_same<StorageAlias, std::string>::value>::type>
const char* c_str() const {
return this->path_.c_str();
}
};
/**
* An iterator over suffixes in a composed path.
*
* PathSuffixIterator always returns RelativePathPiece objects, even when
* iterating over an AbsolutePath. This is intentional, since the suffixes
* are relative to some other location inside the path.
*
* For example, when iterating forwards over the path "foo/bar/baz", the
* iterator yields:
* "foo/bar/baz"
* "bar/baz"
* "baz"
*
* When iterating in reverse it yeilds:
* "baz"
* "bar/baz"
* "foo/bar/baz"
*/
template <bool IsReverse>
class PathSuffixIterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = const RelativePathPiece;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
explicit PathSuffixIterator() {}
explicit PathSuffixIterator(folly::StringPiece path, size_t start = 0)
: path_{path}, start_{start} {}
PathSuffixIterator(const PathSuffixIterator& other) = default;
PathSuffixIterator& operator=(const PathSuffixIterator& other) = default;
static PathIteratorRange<PathSuffixIterator<IsReverse>> createRange(
folly::StringPiece p) {
if (IsReverse) {
auto end = PathSuffixIterator{p, p.size()};
++end;
return PathIteratorRange<PathSuffixIterator<IsReverse>>(
end, PathSuffixIterator{p, folly::StringPiece::npos});
} else {
return PathIteratorRange<PathSuffixIterator<IsReverse>>(
PathSuffixIterator{p}, PathSuffixIterator{p, p.size()});
}
}
bool operator==(const PathSuffixIterator& other) const {
XDCHECK_EQ(path_, other.path_);
// We have to check both start_ and end_ here.
// In most cases start_ will equal other.start_ if and only if end_ equals
// other.end_. However, this is not always true because of end() and
// rend(). end_ points the same place at end() and end() - 1.
// start_ points to the same place at rend() and rend() - 1.
return start_ == other.start_;
}
bool operator!=(const PathSuffixIterator& other) const {
XDCHECK_EQ(path_, other.path_);
return start_ != other.start_;
}
/// ++iter;
PathSuffixIterator& operator++() {
if (IsReverse) {
retreat();
} else {
advance();
}
return *this;
}
/// iter++;
PathSuffixIterator operator++(int) {
PathSuffixIterator tmp(*this);
++(*this); // invoke the ++iter handler above.
return tmp;
}
/// --iter;
PathSuffixIterator& operator--() {
if (IsReverse) {
advance();
} else {
retreat();
}
return *this;
}
/// iter--;
PathSuffixIterator operator--(int) {
PathSuffixIterator tmp(*this);
--(*this); // invoke the --iter handler above.
return tmp;
}
/// Returns the piece for the current iterator position.
RelativePathPiece piece() const {
return RelativePathPiece{
folly::StringPiece{path_.begin() + start_, path_.end()},
SkipPathSanityCheck{}};
}
/*
* Note: dereferencing a PathSuffixIterator returns a new
* RelativePathPiece, and not a reference to an existing RelativePathPiece.
*/
RelativePathPiece operator*() const {
return piece();
}
/*
* TODO: operator->() is not implemented
*
* Since the operator*() returns a new Piece and not a reference,
* operator->() can't really be implemented correctly, as it needs to return
* a pointer to some existing object.
*/
// Piece* operator->() const;
private:
// Move the iterator forwards in the path.
void advance() {
XDCHECK_LT(start_, path_.size());
// npos is used to represent one before the beginning (that is,
// path.rsuffixes().end()). Advance from npos to 0.
if (start_ == folly::StringPiece::npos) {
start_ = 0;
return;
}
// In all other cases, move to just past the next /
auto next = findPathSeparator(path_, start_ + 1);
if (next == folly::StringPiece::npos) {
start_ = path_.size();
} else {
start_ = next + 1;
}
}
// Move the iterator backwards in the path.
void retreat() {
XDCHECK_NE(start_, folly::StringPiece::npos);
// If we are at the start of the string, move to npos
if (start_ == 0) {
start_ = folly::StringPiece::npos;
return;
}
// Otherwise move to just past the previous /
auto next =
rfindPathSeparator(folly::StringPiece{path_.begin(), start_ - 1});
if (next == folly::StringPiece::npos) {
start_ = 0;
} else {
start_ = next + 1;
}
}
/**
* The path we're iterating over.
*/
folly::StringPiece path_;
/**
* Our current position within that path.
*
* This is the start of the current subpiece that we represent. The end of
* the subpiece is always path_.end().
*
* For reverse iteration, we use StringPiece::npos to represent the end.
* That is, when retreat() is called when (start_ == 0), we move to
* StringPiece::npos next.
*/
size_t start_{0};
};
template <typename Storage>
typename RelativePathBase<Storage>::suffix_iterator_range
RelativePathBase<Storage>::suffixes() const {
return suffix_iterator::createRange(this->stringPiece());
}
template <typename Storage>
typename RelativePathBase<Storage>::reverse_suffix_iterator_range
RelativePathBase<Storage>::rsuffixes() const {
return reverse_suffix_iterator::createRange(this->stringPiece());
}
template <typename Storage>
typename AbsolutePathBase<Storage>::suffix_iterator_range
AbsolutePathBase<Storage>::suffixes() const {
// The PathSuffixIterator code assumes that the StringPiece it is given is
// relative, so for absolute paths just strip off the leading directory
// separator.
return suffix_iterator::createRange(this->stringPiece().subpiece(1));
}
template <typename Storage>
typename AbsolutePathBase<Storage>::reverse_suffix_iterator_range
AbsolutePathBase<Storage>::rsuffixes() const {
// The PathSuffixIterator code assumes that the StringPiece it is given is
// relative, so for absolute paths just strip off the leading directory
// separator.
return reverse_suffix_iterator::createRange(this->stringPiece().subpiece(1));
}
// Allow boost to compute hash values
template <typename A>
size_t hash_value(const detail::PathComponentBase<A>& path) {
auto s = path.stringPiece();
return folly::hash::SpookyHashV2::Hash64(s.begin(), s.size(), 0);
}
template <
typename Storage,
typename SanityChecker,
typename Stored,
typename Piece>
size_t hash_value(
const detail::ComposedPathBase<Storage, SanityChecker, Stored, Piece>&
path) {
if (folly::kIsWindows) {
folly::hash::SpookyHashV2 hash{};
for (const auto component : path.components()) {
auto s = component.stringPiece();
hash.Update(s.begin(), s.size());
}
uint64_t hash1, hash2;
hash.Final(&hash1, &hash2);
return hash1;
} else {
auto s = path.stringPiece();
return folly::hash::SpookyHashV2::Hash64(s.begin(), s.size(), 0);
}
}
// Streaming operators for logging and printing
template <typename A>
std::ostream& operator<<(
std::ostream& stream,
const detail::PathComponentBase<A>& a) {
stream << a.stringPiece();
return stream;
}
template <typename A>
std::ostream& operator<<(
std::ostream& stream,
const detail::RelativePathBase<A>& a) {
stream << a.stringPiece();
return stream;
}
template <typename A>
std::ostream& operator<<(
std::ostream& stream,
const detail::AbsolutePathBase<A>& a) {
stream << a.stringPiece();
return stream;
}
// toAppend allows folly::to<> to operate on paths
template <typename A, typename String>
void toAppend(const detail::PathComponentBase<A>& a, String* result) {
toAppend(a.stringPiece(), result);
}
template <typename A, typename String>
void toAppend(const detail::RelativePathBase<A>& a, String* result) {
toAppend(a.stringPiece(), result);
}
template <typename A, typename String>
void toAppend(const detail::AbsolutePathBase<A>& a, String* result) {
toAppend(a.stringPiece(), result);
}
} // namespace detail
// I'm not really a fan of operator overloading, but these
// are reasonably clear in intent; the `+` operator can be used
// to compose certain of the path types together in certain,
// well-defined orders. Composition always yields the Stored flavor
// of the resultant type.
/** Compose two PathComponents to yield a RelativePath */
template <typename A, typename B>
RelativePath operator+(
const detail::PathComponentBase<A>& a,
const detail::PathComponentBase<B>& b) {
// PathComponents can never be empty, so this is always a simple
// join around a "/" character.
return RelativePath(
folly::to<std::string>(
a.stringPiece(), kDirSeparatorStr, b.stringPiece()),
detail::SkipPathSanityCheck());
}
/** Compose a RelativePath with a RelativePath */
template <typename A, typename B>
RelativePath operator+(
const detail::RelativePathBase<A>& a,
const detail::RelativePathBase<B>& b) {
// A RelativePath may be empty, in which case we simply return
// a copy of the other path value.
if (a.stringPiece().empty()) {
return b.copy();
}
if (b.stringPiece().empty()) {
return a.copy();
}
return RelativePath(
folly::to<std::string>(
a.stringPiece(), kDirSeparatorStr, b.stringPiece()),
detail::SkipPathSanityCheck());
}
/** Compose a RelativePath with a PathComponent */
template <typename A, typename B>
RelativePath operator+(
const detail::RelativePathBase<A>& a,
const detail::PathComponentBase<B>& b) {
return a + RelativePathPiece(b);
}
namespace detail {
template <typename Piece, bool IsReverse>
RelativePathPiece ComposedPathIterator<Piece, IsReverse>::remainder() const {
XCHECK_NE(pos_, nullptr);
if (pos_ < path_.end()) {
return RelativePathPiece(
folly::StringPiece(pos_ + 1, path_.end()),
detail::SkipPathSanityCheck());
} else {
return RelativePathPiece();
}
}
} // namespace detail
/**
* Get the current working directory, as an AbsolutePath.
*/
AbsolutePath getcwd();
/**
* Canonicalize a path string.
*
* This removes duplicate "/" characters, and resolves "/./" and "/../"
* components.
*
* Note that we intentially convert a leading "//" to "/
* (e.g., "//foo" --> "/foo"). (POSIX specifies that a leading "//" has
* special platform-defined behavior, so other libraries sometimes leave it
* as-is instead of replacing it with just one "/".)
*
* This is purely a string processing function. The path in question does not
* need to exist. If the path refers to symbolic links, these are not
* resolved.
*
* If the path is relative, the current working directory is prepended to it.
*/
AbsolutePath canonicalPath(folly::StringPiece path);
/**
* Canonicalize a path string relative to absolute path base
*
* If the input is a relative path, the specified base path is prepended to it.
*/
AbsolutePath canonicalPath(folly::StringPiece path, AbsolutePathPiece base);
/**
* Canonicalize a path string relative to a relative path base
*
* Returns a RelativePath that does not start with ".." or fails and returns:
* error code EPERM if path is an absolute path
* error code EXDEV if the return value would start with "../"
* e.g. base="a" and path="../.."
*/
folly::Expected<RelativePath, int> joinAndNormalize(
RelativePathPiece base,
folly::StringPiece path);
/**
* Convert an arbitrary unsanitized input string to a normalized AbsolutePath.
*
* This resolves symlinks, as well as "." and ".." components in the input
* path. If the input path is a relative path it is converted into an absolute
* one.
*
* This will throw an exception if the specified path does not exist, or if it
* or one of its parent directories is inaccessible.
*
* You can use canonicalPath() instead if you just want to normalize the path
* string without attempting to resolve symlinks. canonicalPath() will succeed
* even if the input path does not exist.
*
* You can use normalizeBestEffort() for a hybrid approach that attempts to
* resolve symlinks using realpath() if possible, but falls back to
* canonicalPath() if that fails.
*/
AbsolutePath realpath(const char* path);
AbsolutePath realpath(folly::StringPiece path);
template <typename T>
typename std::enable_if<folly::IsSomeString<T>::value, AbsolutePath>::type
realpath(const T& path) {
return realpath(path.c_str());
}
/** Returns the path to the currently running executable.
* This can fail for example if the executable has been renamed or
* deleted while it is running.
* This path is absolute but is not guaranteed to be canonical
* and my be prone to TOCTOU issues.
*/
AbsolutePath executablePath();
/**
* Convert an arbitrary unsanitized input string to a normalized AbsolutePath.
*
* This is like realpath(), but uses a folly::Expected to return an
* AbsolutePath on success or an errno value on error.
*/
folly::Expected<AbsolutePath, int> realpathExpected(const char* path);
folly::Expected<AbsolutePath, int> realpathExpected(folly::StringPiece path);
template <typename T>
typename std::enable_if<
folly::IsSomeString<T>::value,
folly::Expected<AbsolutePath, int>>::type
realpathExpected(const T& path) {
return realpathExpected(path.c_str());
}
/**
* Return a new path with `~` replaced by the path to the current
* user's home directory. This function does not support expanding
* the home dir of arbitrary users, and will throw an exception
* if the string starts with `~` but not `~/`.
*
* The replacement for `~` is taken from the homeDir parameter
* variable if set. If `!homeDir.has_value()` an exception is thrown.
*
* On successful expansion of the tilde, the resultant path will
* be passed through canonicalPath() and returned.
*
* If path doesn't begin with `~` then it will be passed through
* canonicalPath() and returned.
*
* If the effective home dir value is the empty string, an
* exception is thrown.
*/
AbsolutePath expandUser(
folly::StringPiece path,
std::optional<folly::StringPiece> homeDir = std::nullopt);
/**
* Attempt to normalize a path.
*
* This first attempts to normalize the path using realpath(). However, if
* that fails (for instance, if the specified path does not exist on disk or is
* not accessible), it falls back to using canonicalPath().
*/
AbsolutePath normalizeBestEffort(const char* path);
AbsolutePath normalizeBestEffort(folly::StringPiece path);
template <typename T>
typename std::enable_if<folly::IsSomeString<T>::value, AbsolutePath>::type
normalizeBestEffort(const T& path) {
return normalizeBestEffort(path.c_str());
}
/**
* Splits a path into the first component and the remainder of the path.
* If the path has only one component, the remainder will be empty. If the
* path is empty, an exception is thrown.
*/
std::pair<PathComponentPiece, RelativePathPiece> splitFirst(
RelativePathPiece path);
/**
* Throws std::system_error with ENAMETOOLONG if the given PathComponent is
* longer than kMaxPathComponentLength.
*/
void validatePathComponentLength(PathComponentPiece name);
/**
* Ensure that the specified path exists as a directory.
*
* This creates the specified directory if necessary, creating any parent
* directories as required as well.
*
* Returns true if the directory was created, and false if it already existed.
*
* Throws an exception on error, including if the path or one of its parent
* directories is a file rather than a directory.
*/
bool ensureDirectoryExists(AbsolutePathPiece path);
/**
* Ensure that the specified path exists as a directory.
*
* Sets error code instead of throwing on error.
*/
bool ensureDirectoryExists(
AbsolutePathPiece path,
boost::system::error_code& error) noexcept;
/**
* Recursively remove a directory tree.
*
* Returns false if the directory did not exist in the first place, and true if
* the directory was successfully removed. Throws an exception on error.
*/
bool removeRecursively(AbsolutePathPiece path);
/**
* Remove a file or directory.
*
* Returns false if the file/directory did not exist, and true if it was
* successfully removed. Throws an exception on error.
*/
bool removeFileWithAbsolutePath(AbsolutePathPiece path);
/**
* Rename a file or directory
*
* It will throw an exception on error.
*/
void renameWithAbsolutePath(
AbsolutePathPiece srcPath,
AbsolutePathPiece destPath);
enum class CompareResult {
EQUAL,
BEFORE,
AFTER,
};
/**
* Compare the 2 passed in path based on the case sensitivity.
*
* Returns:
* - CompareResult::EQUAL if both are equal according to the case sensitivity
* - CompareResult::BEFORE if left is lexicographically before right
* - CompareResult::AFTER if left is lexicographically after right
*
*/
CompareResult comparePathComponent(
PathComponentPiece left,
PathComponentPiece right,
CaseSensitivity caseSensitivity);
/**
* Convenient literals for constructing path types.
*/
inline namespace path_literals {
constexpr inline PathComponentPiece operator"" _pc(
const char* str,
size_t len) noexcept {
return PathComponentPiece{folly::StringPiece{str, str + len}};
}
inline RelativePathPiece operator"" _relpath(
const char* str,
size_t len) noexcept {
return RelativePathPiece{folly::StringPiece{str, str + len}};
}
inline AbsolutePathPiece operator"" _abspath(
const char* str,
size_t len) noexcept {
return AbsolutePathPiece{folly::StringPiece{str, str + len}};
}
} // namespace path_literals
/**
* Gets memory usage of the path inside the RelativePathBase
*/
template <typename StringType>
size_t estimateIndirectMemoryUsage(
const detail::RelativePathBase<StringType>& path) {
return estimateIndirectMemoryUsage(path.value());
}
} // namespace eden
} // namespace facebook
namespace std {
/* Allow std::hash to operate on these types */
template <typename A>
struct hash<facebook::eden::detail::PathComponentBase<A>> {
size_t operator()(
const facebook::eden::detail::PathComponentBase<A>& s) const {
return facebook::eden::detail::hash_value(s);
}
};
template <typename A>
struct hash<facebook::eden::detail::RelativePathBase<A>> {
size_t operator()(
const facebook::eden::detail::RelativePathBase<A>& s) const {
return facebook::eden::detail::hash_value(s);
}
};
template <typename A>
struct hash<facebook::eden::detail::AbsolutePathBase<A>> {
size_t operator()(
const facebook::eden::detail::AbsolutePathBase<A>& s) const {
return facebook::eden::detail::hash_value(s);
}
};
} // namespace std
namespace fmt {
template <typename Storage>
struct formatter<facebook::eden::detail::PathComponentBase<Storage>>
: formatter<string_view> {
using Path = facebook::eden::detail::PathComponentBase<Storage>;
auto format(const Path& p, format_context& ctx) {
auto sp = p.stringPiece();
return formatter<string_view>::format({sp.data(), sp.size()}, ctx);
}
};
template <typename Storage>
struct formatter<facebook::eden::detail::AbsolutePathBase<Storage>>
: formatter<string_view> {
using Path = facebook::eden::detail::AbsolutePathBase<Storage>;
auto format(const Path& p, format_context& ctx) {
auto sp = p.stringPiece();
return formatter<string_view>::format({sp.data(), sp.size()}, ctx);
}
};
template <typename Storage>
struct formatter<facebook::eden::detail::RelativePathBase<Storage>>
: formatter<string_view> {
using Path = facebook::eden::detail::RelativePathBase<Storage>;
auto format(const Path& p, format_context& ctx) {
auto sp = p.stringPiece();
return formatter<string_view>::format({sp.data(), sp.size()}, ctx);
}
};
} // namespace fmt
namespace folly {
/*
* folly::FormatValue specializations so that path types can be used with
* folly::format()
*
* Unfortunately due to the way FormatValue is implemented we have to provide
* explicit specializations for the individual subclasses (we can't just
* partially specialize it once for PathBase).
*
* The object being formatted is guaranteed to live longer than the FormatValue
* itself, so we only need to capture the StringPiece in the constructor.
*
* We defer all implementation to FormatValue<StringPiece>
*/
template <typename Storage>
class FormatValue<facebook::eden::detail::PathComponentBase<Storage>>
: public FormatValue<StringPiece> {
public:
using Param = facebook::eden::detail::PathComponentBase<Storage>;
explicit FormatValue(const Param& val)
: FormatValue<StringPiece>(val.stringPiece()) {}
};
template <typename Storage>
class FormatValue<facebook::eden::detail::RelativePathBase<Storage>>
: public FormatValue<StringPiece> {
public:
using Param = facebook::eden::detail::RelativePathBase<Storage>;
explicit FormatValue(const Param& val)
: FormatValue<StringPiece>(val.stringPiece()) {}
};
template <typename Storage>
class FormatValue<facebook::eden::detail::AbsolutePathBase<Storage>>
: public FormatValue<StringPiece> {
public:
using Param = facebook::eden::detail::AbsolutePathBase<Storage>;
explicit FormatValue(const Param& val)
: FormatValue<StringPiece>(val.stringPiece()) {}
};
} // namespace folly