Implement filesystem NB comments, relative paths, and related issues.
This is a fairly large patch that implements all of the filesystem NB comments
and the relative paths changes (ex. adding weakly_canonical). These issues
and papers are all interrelated so their implementation couldn't be split up
nicely.
This patch upgrades <experimental/filesystem> to match the C++17 spec and not
the published experimental TS spec. Some of the changes in this patch are both
API and ABI breaking, however libc++ makes no guarantee about stability for
experimental implementations.
The major changes in this patch are:
* Implement NB comments for filesystem (P0492R2), including:
* Implement `perm_options` enum as part of NB comments, and update the
`permissions` function to match.
* Implement changes to `remove_filename` and `replace_filename`
* Implement changes to `path::stem()` and `path::extension()` which support
splitting examples like `.profile`.
* Change path iteration to return an empty path instead of '.' for trailing
separators.
* Change `operator/=` to handle absolute paths on the RHS.
* Change `absolute` to no longer accept a current path argument.
* Implement relative paths according to NB comments (P0219r1)
* Combine `path.cpp` and `operations.cpp` since some path functions require
access to the operations internals, and some fs operations require access
to the path parser.
git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@329028 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -68,7 +68,7 @@ set(BENCHMARK_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(BENCHMARK_LIBCXX_INSTALL ${CMAKE_CURRENT_BINARY_DIR}/benchmark-libcxx)
|
||||
set(BENCHMARK_NATIVE_INSTALL ${CMAKE_CURRENT_BINARY_DIR}/benchmark-native)
|
||||
set(BENCHMARK_TEST_COMPILE_FLAGS
|
||||
-std=c++14 -O2
|
||||
-std=c++17 -O2
|
||||
-I${BENCHMARK_LIBCXX_INSTALL}/include
|
||||
-I${LIBCXX_SOURCE_DIR}/test/support
|
||||
)
|
||||
|
||||
@@ -29,14 +29,16 @@ inline std::default_random_engine& getRandomEngine() {
|
||||
return RandEngine;
|
||||
}
|
||||
|
||||
|
||||
inline char getRandomChar() {
|
||||
std::uniform_int_distribution<> LettersDist(0, LettersSize-1);
|
||||
return Letters[LettersDist(getRandomEngine())];
|
||||
}
|
||||
|
||||
template <class IntT>
|
||||
inline IntT getRandomInteger() {
|
||||
std::uniform_int_distribution<IntT> dist;
|
||||
inline IntT getRandomInteger(IntT Min = 0,
|
||||
IntT Max = std::numeric_limits<IntT>::max()) {
|
||||
std::uniform_int_distribution<IntT> dist(Min, Max);
|
||||
return dist(getRandomEngine());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
#include <experimental/filesystem>
|
||||
|
||||
#include "benchmark/benchmark.h"
|
||||
#include "GenerateInput.hpp"
|
||||
#include "test_iterators.h"
|
||||
|
||||
namespace fs = std::experimental::filesystem;
|
||||
#include "filesystem_include.hpp"
|
||||
|
||||
static const size_t TestNumInputs = 1024;
|
||||
|
||||
|
||||
template <class GenInputs>
|
||||
void BM_PathConstructString(benchmark::State &st, GenInputs gen) {
|
||||
using namespace fs;
|
||||
using fs::path;
|
||||
const auto in = gen(st.range(0));
|
||||
path PP;
|
||||
for (auto& Part : in)
|
||||
@@ -21,14 +18,15 @@ void BM_PathConstructString(benchmark::State &st, GenInputs gen) {
|
||||
const path P(PP.native());
|
||||
benchmark::DoNotOptimize(P.native().data());
|
||||
}
|
||||
st.SetComplexityN(st.range(0));
|
||||
}
|
||||
BENCHMARK_CAPTURE(BM_PathConstructString, large_string,
|
||||
getRandomStringInputs)->Arg(TestNumInputs);
|
||||
getRandomStringInputs)->Range(8, TestNumInputs)->Complexity();
|
||||
|
||||
|
||||
template <class GenInputs>
|
||||
void BM_PathConstructCStr(benchmark::State &st, GenInputs gen) {
|
||||
using namespace fs;
|
||||
using fs::path;
|
||||
const auto in = gen(st.range(0));
|
||||
path PP;
|
||||
for (auto& Part : in)
|
||||
@@ -45,7 +43,7 @@ BENCHMARK_CAPTURE(BM_PathConstructCStr, large_string,
|
||||
|
||||
template <template <class...> class ItType, class GenInputs>
|
||||
void BM_PathConstructIter(benchmark::State &st, GenInputs gen) {
|
||||
using namespace fs;
|
||||
using fs::path;
|
||||
using Iter = ItType<std::string::const_iterator>;
|
||||
const auto in = gen(st.range(0));
|
||||
path PP;
|
||||
@@ -60,6 +58,7 @@ void BM_PathConstructIter(benchmark::State &st, GenInputs gen) {
|
||||
const path P(Start, End);
|
||||
benchmark::DoNotOptimize(P.native().data());
|
||||
}
|
||||
st.SetComplexityN(st.range(0));
|
||||
}
|
||||
template <class GenInputs>
|
||||
void BM_PathConstructInputIter(benchmark::State &st, GenInputs gen) {
|
||||
@@ -70,14 +69,14 @@ void BM_PathConstructForwardIter(benchmark::State &st, GenInputs gen) {
|
||||
BM_PathConstructIter<forward_iterator>(st, gen);
|
||||
}
|
||||
BENCHMARK_CAPTURE(BM_PathConstructInputIter, large_string,
|
||||
getRandomStringInputs)->Arg(TestNumInputs);
|
||||
getRandomStringInputs)->Range(8, TestNumInputs)->Complexity();
|
||||
BENCHMARK_CAPTURE(BM_PathConstructForwardIter, large_string,
|
||||
getRandomStringInputs)->Arg(TestNumInputs);
|
||||
getRandomStringInputs)->Range(8, TestNumInputs)->Complexity();
|
||||
|
||||
|
||||
template <class GenInputs>
|
||||
void BM_PathIterateMultipleTimes(benchmark::State &st, GenInputs gen) {
|
||||
using namespace fs;
|
||||
using fs::path;
|
||||
const auto in = gen(st.range(0));
|
||||
path PP;
|
||||
for (auto& Part : in)
|
||||
@@ -89,14 +88,15 @@ void BM_PathIterateMultipleTimes(benchmark::State &st, GenInputs gen) {
|
||||
}
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
st.SetComplexityN(st.range(0));
|
||||
}
|
||||
BENCHMARK_CAPTURE(BM_PathIterateMultipleTimes, iterate_elements,
|
||||
getRandomStringInputs)->Arg(TestNumInputs);
|
||||
getRandomStringInputs)->Range(8, TestNumInputs)->Complexity();
|
||||
|
||||
|
||||
template <class GenInputs>
|
||||
void BM_PathIterateOnce(benchmark::State &st, GenInputs gen) {
|
||||
using namespace fs;
|
||||
using fs::path;
|
||||
const auto in = gen(st.range(0));
|
||||
path PP;
|
||||
for (auto& Part : in)
|
||||
@@ -109,13 +109,14 @@ void BM_PathIterateOnce(benchmark::State &st, GenInputs gen) {
|
||||
}
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
st.SetComplexityN(st.range(0));
|
||||
}
|
||||
BENCHMARK_CAPTURE(BM_PathIterateOnce, iterate_elements,
|
||||
getRandomStringInputs)->Arg(TestNumInputs);
|
||||
getRandomStringInputs)->Range(8, TestNumInputs)->Complexity();
|
||||
|
||||
template <class GenInputs>
|
||||
void BM_PathIterateOnceBackwards(benchmark::State &st, GenInputs gen) {
|
||||
using namespace fs;
|
||||
using fs::path;
|
||||
const auto in = gen(st.range(0));
|
||||
path PP;
|
||||
for (auto& Part : in)
|
||||
@@ -135,4 +136,28 @@ void BM_PathIterateOnceBackwards(benchmark::State &st, GenInputs gen) {
|
||||
BENCHMARK_CAPTURE(BM_PathIterateOnceBackwards, iterate_elements,
|
||||
getRandomStringInputs)->Arg(TestNumInputs);
|
||||
|
||||
static fs::path getRandomPaths(int NumParts, int PathLen) {
|
||||
fs::path Result;
|
||||
while (NumParts--) {
|
||||
std::string Part = getRandomString(PathLen);
|
||||
Result /= Part;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
template <class GenInput>
|
||||
void BM_LexicallyNormal(benchmark::State &st, GenInput gen, size_t PathLen) {
|
||||
using fs::path;
|
||||
auto In = gen(st.range(0), PathLen);
|
||||
benchmark::DoNotOptimize(&In);
|
||||
while (st.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(In.lexically_normal());
|
||||
}
|
||||
st.SetComplexityN(st.range(0));
|
||||
}
|
||||
BENCHMARK_CAPTURE(BM_LexicallyNormal, small_path,
|
||||
getRandomPaths, /*PathLen*/5)->RangeMultiplier(2)->Range(2, 256)->Complexity();
|
||||
BENCHMARK_CAPTURE(BM_LexicallyNormal, large_path,
|
||||
getRandomPaths, /*PathLen*/32)->RangeMultiplier(2)->Range(2, 256)->Complexity();
|
||||
|
||||
BENCHMARK_MAIN();
|
||||
|
||||
@@ -76,11 +76,11 @@
|
||||
|
||||
// operational functions
|
||||
|
||||
path absolute(const path& p, const path& base=current_path());
|
||||
path absolute(const path& p);
|
||||
path absolute(const path& p, error_code &ec);
|
||||
|
||||
path canonical(const path& p, const path& base = current_path());
|
||||
path canonical(const path& p);
|
||||
path canonical(const path& p, error_code& ec);
|
||||
path canonical(const path& p, const path& base, error_code& ec);
|
||||
|
||||
void copy(const path& from, const path& to);
|
||||
void copy(const path& from, const path& to, error_code& ec);
|
||||
@@ -185,9 +185,17 @@
|
||||
void permissions(const path& p, perms prms, perm_options opts,
|
||||
error_code& ec);
|
||||
|
||||
path proximate(const path& p, error_code& ec);
|
||||
path proximate(const path& p, const path& base = current_path());
|
||||
path proximate(const path& p, const path& base, error_code &ec);
|
||||
|
||||
path read_symlink(const path& p);
|
||||
path read_symlink(const path& p, error_code& ec);
|
||||
|
||||
path relative(const path& p, error_code& ec);
|
||||
path relative(const path& p, const path& base=current_path());
|
||||
path relative(const path& p, const path& base, error_code& ec);
|
||||
|
||||
bool remove(const path& p);
|
||||
bool remove(const path& p, error_code& ec) _NOEXCEPT;
|
||||
|
||||
@@ -211,12 +219,13 @@
|
||||
file_status symlink_status(const path& p);
|
||||
file_status symlink_status(const path& p, error_code& ec) _NOEXCEPT;
|
||||
|
||||
path system_complete(const path& p);
|
||||
path system_complete(const path& p, error_code& ec);
|
||||
|
||||
path temp_directory_path();
|
||||
path temp_directory_path(error_code& ec);
|
||||
|
||||
path weakly_canonical(path const& p);
|
||||
path weakly_canonical(path const& p, error_code& ec);
|
||||
|
||||
|
||||
} } } } // namespaces std::experimental::filesystem::v1
|
||||
|
||||
*/
|
||||
@@ -796,26 +805,26 @@ public:
|
||||
|
||||
private:
|
||||
template <class _ECharT>
|
||||
void __append_sep_if_needed(_ECharT __first_or_null) {
|
||||
const _ECharT __null_val = {};
|
||||
bool __append_sep = !empty() &&
|
||||
!__is_separator(__pn_.back()) &&
|
||||
__first_or_null != __null_val && // non-empty
|
||||
!__is_separator(__first_or_null);
|
||||
if (__append_sep)
|
||||
__pn_ += preferred_separator;
|
||||
static bool __source_is_absolute(_ECharT __first_or_null) {
|
||||
return __is_separator(__first_or_null);
|
||||
}
|
||||
|
||||
public:
|
||||
// appends
|
||||
path& operator/=(const path& __p) {
|
||||
_LIBCPP_ASSERT(!__p.has_root_name(),
|
||||
"cannot append to a path with a root name");
|
||||
__append_sep_if_needed(__p.empty() ? char{} : __p.__pn_[0]);
|
||||
if (__p.is_absolute()) {
|
||||
__pn_ = __p.__pn_;
|
||||
return *this;
|
||||
}
|
||||
if (has_filename())
|
||||
__pn_ += preferred_separator;
|
||||
__pn_ += __p.native();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// FIXME: Use _LIBCPP_DIAGNOSE_WARNING to produce a diagnostic when __src
|
||||
// is known at compile time to be "/' since the user almost certainly intended
|
||||
// to append a separator instead of overwriting the path with "/"
|
||||
template <class _Source>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
_EnableIfPathable<_Source>
|
||||
@@ -828,7 +837,10 @@ public:
|
||||
append(const _Source& __src) {
|
||||
using _Traits = __is_pathable<_Source>;
|
||||
using _CVT = _PathCVT<_SourceChar<_Source>>;
|
||||
__append_sep_if_needed(_Traits::__first_or_null(__src));
|
||||
if (__source_is_absolute(_Traits::__first_or_null(__src)))
|
||||
__pn_.clear();
|
||||
else if (has_filename())
|
||||
__pn_ += preferred_separator;
|
||||
_CVT::__append_source(__pn_, __src);
|
||||
return *this;
|
||||
}
|
||||
@@ -838,10 +850,11 @@ public:
|
||||
typedef typename iterator_traits<_InputIt>::value_type _ItVal;
|
||||
static_assert(__can_convert_char<_ItVal>::value, "Must convertible");
|
||||
using _CVT = _PathCVT<_ItVal>;
|
||||
if (__first != __last) {
|
||||
__append_sep_if_needed(*__first);
|
||||
_CVT::__append_range(__pn_, __first, __last);
|
||||
}
|
||||
if (__first != __last && __source_is_absolute(*__first))
|
||||
__pn_.clear();
|
||||
else if (has_filename())
|
||||
__pn_ += preferred_separator;
|
||||
_CVT::__append_range(__pn_, __first, __last);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -916,10 +929,9 @@ public:
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
path& remove_filename() {
|
||||
if (__pn_.size() == __root_path_raw().size())
|
||||
clear();
|
||||
else
|
||||
__pn_ = __parent_path();
|
||||
auto __fname = __filename();
|
||||
if (!__fname.empty())
|
||||
__pn_.erase(__fname.data() - __pn_.data());
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -935,6 +947,10 @@ public:
|
||||
__pn_.swap(__rhs.__pn_);
|
||||
}
|
||||
|
||||
// private helper to allow reserving memory in the path
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void __reserve(size_t __s) { __pn_.reserve(__s); }
|
||||
|
||||
// native format observers
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
const string_type& native() const _NOEXCEPT {
|
||||
@@ -1023,6 +1039,17 @@ public:
|
||||
_LIBCPP_INLINE_VISIBILITY bool is_absolute() const { return has_root_directory(); }
|
||||
_LIBCPP_INLINE_VISIBILITY bool is_relative() const { return !is_absolute(); }
|
||||
|
||||
// relative paths
|
||||
path lexically_normal() const;
|
||||
path lexically_relative(const path& __base) const;
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY path lexically_proximate(const path& __base) const {
|
||||
path __result = this->lexically_relative(__base);
|
||||
if (__result.native().empty())
|
||||
return *this;
|
||||
return __result;
|
||||
}
|
||||
|
||||
// iterators
|
||||
class _LIBCPP_TYPE_VIS iterator;
|
||||
typedef iterator const_iterator;
|
||||
@@ -1277,7 +1304,9 @@ void __throw_filesystem_error(_Args&&...)
|
||||
// operational functions
|
||||
|
||||
_LIBCPP_FUNC_VIS
|
||||
path __canonical(const path&, const path&, error_code *__ec=nullptr);
|
||||
path __absolute(const path&, error_code *__ec=nullptr);
|
||||
_LIBCPP_FUNC_VIS
|
||||
path __canonical(const path&, error_code *__ec=nullptr);
|
||||
_LIBCPP_FUNC_VIS
|
||||
void __copy(const path& __from, const path& __to, copy_options __opt,
|
||||
error_code *__ec=nullptr);
|
||||
@@ -1342,6 +1371,8 @@ _LIBCPP_FUNC_VIS
|
||||
path __system_complete(const path&, error_code *__ec=nullptr);
|
||||
_LIBCPP_FUNC_VIS
|
||||
path __temp_directory_path(error_code *__ec=nullptr);
|
||||
_LIBCPP_FUNC_VIS
|
||||
path __weakly_canonical(path const& __p, error_code *__ec=nullptr);
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path current_path() {
|
||||
@@ -1363,24 +1394,24 @@ void current_path(const path& __p, error_code& __ec) _NOEXCEPT {
|
||||
__current_path(__p, &__ec);
|
||||
}
|
||||
|
||||
_LIBCPP_FUNC_VIS
|
||||
path absolute(const path&, const path& __p2 = current_path());
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path absolute(const path& __p) {
|
||||
return __absolute(__p);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path canonical(const path& __p, const path& __base = current_path()) {
|
||||
return __canonical(__p, __base);
|
||||
path absolute(const path& __p, error_code &__ec) {
|
||||
return __absolute(__p, &__ec);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path canonical(const path& __p) {
|
||||
return __canonical(__p);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path canonical(const path& __p, error_code& __ec) {
|
||||
path __base = __current_path(&__ec);
|
||||
if (__ec) return {};
|
||||
return __canonical(__p, __base, &__ec);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path canonical(const path& __p, const path& __base, error_code& __ec) {
|
||||
return __canonical(__p, __base, &__ec);
|
||||
return __canonical(__p, &__ec);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
@@ -1492,7 +1523,8 @@ void create_symlink(const path& __to, const path& __new) {
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void create_symlink(const path& __to, const path& __new, error_code& __ec) _NOEXCEPT {
|
||||
void create_symlink(const path& __to, const path& __new,
|
||||
error_code& __ec) _NOEXCEPT {
|
||||
return __create_symlink(__to, __new, &__ec);
|
||||
}
|
||||
|
||||
@@ -1715,6 +1747,27 @@ void permissions(const path& __p, perms __prms, perm_options __opts,
|
||||
__permissions(__p, __prms, __opts, &__ec);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path proximate(const path& __p, const path& __base, error_code& __ec) {
|
||||
path __tmp = __weakly_canonical(__p, &__ec);
|
||||
if (__ec)
|
||||
return {};
|
||||
path __tmp_base = __weakly_canonical(__base, &__ec);
|
||||
if (__ec)
|
||||
return {};
|
||||
return __tmp.lexically_proximate(__tmp_base);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path proximate(const path& __p, error_code& __ec) {
|
||||
return proximate(__p, current_path(), __ec);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path proximate(const path& __p, const path& __base = current_path()) {
|
||||
return __weakly_canonical(__p).lexically_proximate(__weakly_canonical(__base));
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path read_symlink(const path& __p) {
|
||||
return __read_symlink(__p);
|
||||
@@ -1725,6 +1778,29 @@ path read_symlink(const path& __p, error_code& __ec) {
|
||||
return __read_symlink(__p, &__ec);
|
||||
}
|
||||
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path relative(const path& __p, const path& __base, error_code& __ec) {
|
||||
path __tmp = __weakly_canonical(__p, &__ec);
|
||||
if (__ec)
|
||||
return path();
|
||||
path __tmpbase = __weakly_canonical(__base, &__ec);
|
||||
if (__ec)
|
||||
return path();
|
||||
return __tmp.lexically_relative(__tmpbase);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path relative(const path& __p, error_code& __ec) {
|
||||
return relative(__p, current_path(), __ec);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path relative(const path& __p, const path& __base=current_path()) {
|
||||
return __weakly_canonical(__p).lexically_relative(__weakly_canonical(__base));
|
||||
}
|
||||
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
bool remove(const path& __p) {
|
||||
return __remove(__p);
|
||||
@@ -1795,16 +1871,6 @@ file_status symlink_status(const path& __p, error_code& __ec) _NOEXCEPT {
|
||||
return __symlink_status(__p, &__ec);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path system_complete(const path& __p) {
|
||||
return __system_complete(__p);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path system_complete(const path& __p, error_code& __ec) {
|
||||
return __system_complete(__p, &__ec);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path temp_directory_path() {
|
||||
return __temp_directory_path();
|
||||
@@ -1815,6 +1881,16 @@ path temp_directory_path(error_code& __ec) {
|
||||
return __temp_directory_path(&__ec);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path weakly_canonical(path const& __p) {
|
||||
return __weakly_canonical(__p);
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
path weakly_canonical(path const& __p, error_code& __ec) {
|
||||
return __weakly_canonical(__p, &__ec);
|
||||
}
|
||||
|
||||
|
||||
class directory_entry
|
||||
{
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
#include "experimental/filesystem"
|
||||
#include "iterator"
|
||||
#include "fstream"
|
||||
#include "type_traits"
|
||||
#include "random" /* for unique_path */
|
||||
#include "string_view"
|
||||
#include "type_traits"
|
||||
#include "vector"
|
||||
#include "cstdlib"
|
||||
#include "climits"
|
||||
|
||||
@@ -57,6 +59,250 @@ _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
|
||||
filesystem_error::~filesystem_error() {}
|
||||
|
||||
|
||||
namespace { namespace parser
|
||||
{
|
||||
|
||||
using string_view_t = path::__string_view;
|
||||
using string_view_pair = pair<string_view_t, string_view_t>;
|
||||
using PosPtr = path::value_type const*;
|
||||
|
||||
struct PathParser {
|
||||
enum ParserState : unsigned char {
|
||||
// Zero is a special sentinel value used by default constructed iterators.
|
||||
PS_BeforeBegin = 1,
|
||||
PS_InRootName,
|
||||
PS_InRootDir,
|
||||
PS_InFilenames,
|
||||
PS_InTrailingSep,
|
||||
PS_AtEnd
|
||||
};
|
||||
|
||||
const string_view_t Path;
|
||||
string_view_t RawEntry;
|
||||
ParserState State;
|
||||
|
||||
private:
|
||||
PathParser(string_view_t P, ParserState State) noexcept
|
||||
: Path(P), State(State) {}
|
||||
|
||||
public:
|
||||
PathParser(string_view_t P, string_view_t E, unsigned char S)
|
||||
: Path(P), RawEntry(E), State(static_cast<ParserState>(S)) {
|
||||
// S cannot be '0' or PS_BeforeBegin.
|
||||
}
|
||||
|
||||
static PathParser CreateBegin(string_view_t P) noexcept {
|
||||
PathParser PP(P, PS_BeforeBegin);
|
||||
PP.increment();
|
||||
return PP;
|
||||
}
|
||||
|
||||
static PathParser CreateEnd(string_view_t P) noexcept {
|
||||
PathParser PP(P, PS_AtEnd);
|
||||
return PP;
|
||||
}
|
||||
|
||||
PosPtr peek() const noexcept {
|
||||
auto TkEnd = getNextTokenStartPos();
|
||||
auto End = getAfterBack();
|
||||
return TkEnd == End ? nullptr : TkEnd;
|
||||
}
|
||||
|
||||
void increment() noexcept {
|
||||
const PosPtr End = getAfterBack();
|
||||
const PosPtr Start = getNextTokenStartPos();
|
||||
if (Start == End)
|
||||
return makeState(PS_AtEnd);
|
||||
|
||||
switch (State) {
|
||||
case PS_BeforeBegin: {
|
||||
PosPtr TkEnd = consumeSeparator(Start, End);
|
||||
if (TkEnd)
|
||||
return makeState(PS_InRootDir, Start, TkEnd);
|
||||
else
|
||||
return makeState(PS_InFilenames, Start, consumeName(Start, End));
|
||||
}
|
||||
case PS_InRootDir:
|
||||
return makeState(PS_InFilenames, Start, consumeName(Start, End));
|
||||
|
||||
case PS_InFilenames: {
|
||||
PosPtr SepEnd = consumeSeparator(Start, End);
|
||||
if (SepEnd != End) {
|
||||
PosPtr TkEnd = consumeName(SepEnd, End);
|
||||
if (TkEnd)
|
||||
return makeState(PS_InFilenames, SepEnd, TkEnd);
|
||||
}
|
||||
return makeState(PS_InTrailingSep, Start, SepEnd);
|
||||
}
|
||||
|
||||
case PS_InTrailingSep:
|
||||
return makeState(PS_AtEnd);
|
||||
|
||||
case PS_InRootName:
|
||||
case PS_AtEnd:
|
||||
_LIBCPP_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void decrement() noexcept {
|
||||
const PosPtr REnd = getBeforeFront();
|
||||
const PosPtr RStart = getCurrentTokenStartPos() - 1;
|
||||
if (RStart == REnd) // we're decrementing the begin
|
||||
return makeState(PS_BeforeBegin);
|
||||
|
||||
switch (State) {
|
||||
case PS_AtEnd: {
|
||||
// Try to consume a trailing separator or root directory first.
|
||||
if (PosPtr SepEnd = consumeSeparator(RStart, REnd)) {
|
||||
if (SepEnd == REnd)
|
||||
return makeState(PS_InRootDir, Path.data(), RStart + 1);
|
||||
return makeState(PS_InTrailingSep, SepEnd + 1, RStart + 1);
|
||||
} else {
|
||||
PosPtr TkStart = consumeName(RStart, REnd);
|
||||
return makeState(PS_InFilenames, TkStart + 1, RStart + 1);
|
||||
}
|
||||
}
|
||||
case PS_InTrailingSep:
|
||||
return makeState(PS_InFilenames, consumeName(RStart, REnd) + 1, RStart + 1);
|
||||
case PS_InFilenames: {
|
||||
PosPtr SepEnd = consumeSeparator(RStart, REnd);
|
||||
if (SepEnd == REnd)
|
||||
return makeState(PS_InRootDir, Path.data(), RStart + 1);
|
||||
PosPtr TkEnd = consumeName(SepEnd, REnd);
|
||||
return makeState(PS_InFilenames, TkEnd + 1, SepEnd + 1);
|
||||
}
|
||||
case PS_InRootDir:
|
||||
// return makeState(PS_InRootName, Path.data(), RStart + 1);
|
||||
case PS_InRootName:
|
||||
case PS_BeforeBegin:
|
||||
_LIBCPP_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Return a view with the "preferred representation" of the current
|
||||
/// element. For example trailing separators are represented as a '.'
|
||||
string_view_t operator*() const noexcept {
|
||||
switch (State) {
|
||||
case PS_BeforeBegin:
|
||||
case PS_AtEnd:
|
||||
return "";
|
||||
case PS_InRootDir:
|
||||
return "/";
|
||||
case PS_InTrailingSep:
|
||||
return "";
|
||||
case PS_InRootName:
|
||||
case PS_InFilenames:
|
||||
return RawEntry;
|
||||
}
|
||||
_LIBCPP_UNREACHABLE();
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return State != PS_BeforeBegin && State != PS_AtEnd;
|
||||
}
|
||||
|
||||
PathParser& operator++() noexcept {
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
PathParser& operator--() noexcept {
|
||||
decrement();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool inRootPath() const noexcept {
|
||||
return State == PS_InRootDir || State == PS_InRootName;
|
||||
}
|
||||
|
||||
private:
|
||||
void makeState(ParserState NewState, PosPtr Start, PosPtr End) noexcept {
|
||||
State = NewState;
|
||||
RawEntry = string_view_t(Start, End - Start);
|
||||
}
|
||||
void makeState(ParserState NewState) noexcept {
|
||||
State = NewState;
|
||||
RawEntry = {};
|
||||
}
|
||||
|
||||
PosPtr getAfterBack() const noexcept {
|
||||
return Path.data() + Path.size();
|
||||
}
|
||||
|
||||
PosPtr getBeforeFront() const noexcept {
|
||||
return Path.data() - 1;
|
||||
}
|
||||
|
||||
/// \brief Return a pointer to the first character after the currently
|
||||
/// lexed element.
|
||||
PosPtr getNextTokenStartPos() const noexcept {
|
||||
switch (State) {
|
||||
case PS_BeforeBegin:
|
||||
return Path.data();
|
||||
case PS_InRootName:
|
||||
case PS_InRootDir:
|
||||
case PS_InFilenames:
|
||||
return &RawEntry.back() + 1;
|
||||
case PS_InTrailingSep:
|
||||
case PS_AtEnd:
|
||||
return getAfterBack();
|
||||
}
|
||||
_LIBCPP_UNREACHABLE();
|
||||
}
|
||||
|
||||
/// \brief Return a pointer to the first character in the currently lexed
|
||||
/// element.
|
||||
PosPtr getCurrentTokenStartPos() const noexcept {
|
||||
switch (State) {
|
||||
case PS_BeforeBegin:
|
||||
case PS_InRootName:
|
||||
return &Path.front();
|
||||
case PS_InRootDir:
|
||||
case PS_InFilenames:
|
||||
case PS_InTrailingSep:
|
||||
return &RawEntry.front();
|
||||
case PS_AtEnd:
|
||||
return &Path.back() + 1;
|
||||
}
|
||||
_LIBCPP_UNREACHABLE();
|
||||
}
|
||||
|
||||
PosPtr consumeSeparator(PosPtr P, PosPtr End) const noexcept {
|
||||
if (P == End || *P != '/')
|
||||
return nullptr;
|
||||
const int Inc = P < End ? 1 : -1;
|
||||
P += Inc;
|
||||
while (P != End && *P == '/')
|
||||
P += Inc;
|
||||
return P;
|
||||
}
|
||||
|
||||
PosPtr consumeName(PosPtr P, PosPtr End) const noexcept {
|
||||
if (P == End || *P == '/')
|
||||
return nullptr;
|
||||
const int Inc = P < End ? 1 : -1;
|
||||
P += Inc;
|
||||
while (P != End && *P != '/')
|
||||
P += Inc;
|
||||
return P;
|
||||
}
|
||||
};
|
||||
|
||||
string_view_pair separate_filename(string_view_t const & s) {
|
||||
if (s == "." || s == ".." || s.empty()) return string_view_pair{s, ""};
|
||||
auto pos = s.find_last_of('.');
|
||||
if (pos == string_view_t::npos || pos == 0)
|
||||
return string_view_pair{s, string_view_t{}};
|
||||
return string_view_pair{s.substr(0, pos), s.substr(pos)};
|
||||
}
|
||||
|
||||
string_view_t createView(PosPtr S, PosPtr E) noexcept {
|
||||
return {S, static_cast<size_t>(E - S) + 1};
|
||||
}
|
||||
|
||||
}} // namespace parser
|
||||
|
||||
|
||||
// POSIX HELPERS
|
||||
|
||||
namespace detail { namespace {
|
||||
@@ -186,14 +432,33 @@ bool copy_file_impl(const path& from, const path& to, perms from_perms,
|
||||
}} // end namespace detail
|
||||
|
||||
using detail::set_or_throw;
|
||||
using parser::string_view_t;
|
||||
using parser::PathParser;
|
||||
using parser::createView;
|
||||
|
||||
path __canonical(path const & orig_p, const path& base, std::error_code *ec)
|
||||
static path __do_absolute(const path& p, path *cwd, std::error_code *ec) {
|
||||
if (ec) ec->clear();
|
||||
if (p.is_absolute())
|
||||
return p;
|
||||
*cwd = __current_path(ec);
|
||||
if (ec && *ec)
|
||||
return {};
|
||||
return (*cwd) / p;
|
||||
}
|
||||
|
||||
path __absolute(const path& p, std::error_code *ec) {
|
||||
path cwd;
|
||||
return __do_absolute(p, &cwd, ec);
|
||||
}
|
||||
|
||||
path __canonical(path const & orig_p, std::error_code *ec)
|
||||
{
|
||||
path p = absolute(orig_p, base);
|
||||
path cwd;
|
||||
path p = __do_absolute(orig_p, &cwd, ec);
|
||||
char buff[PATH_MAX + 1];
|
||||
char *ret;
|
||||
if ((ret = ::realpath(p.c_str(), buff)) == nullptr) {
|
||||
set_or_throw(ec, "canonical", orig_p, base);
|
||||
set_or_throw(ec, "canonical", orig_p, cwd);
|
||||
return {};
|
||||
}
|
||||
if (ec) ec->clear();
|
||||
@@ -791,11 +1056,6 @@ file_status __symlink_status(const path& p, std::error_code *ec) {
|
||||
return detail::posix_lstat(p, ec);
|
||||
}
|
||||
|
||||
path __system_complete(const path& p, std::error_code *ec) {
|
||||
if (ec) ec->clear();
|
||||
return absolute(p, current_path());
|
||||
}
|
||||
|
||||
path __temp_directory_path(std::error_code* ec) {
|
||||
const char* env_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
|
||||
const char* ret = nullptr;
|
||||
@@ -820,34 +1080,393 @@ path __temp_directory_path(std::error_code* ec) {
|
||||
return p;
|
||||
}
|
||||
|
||||
// An absolute path is composed according to the table in [fs.op.absolute].
|
||||
path absolute(const path& p, const path& base) {
|
||||
auto root_name = p.root_name();
|
||||
auto root_dir = p.root_directory();
|
||||
|
||||
if (!root_name.empty() && !root_dir.empty())
|
||||
return p;
|
||||
path __weakly_canonical(const path& p, std::error_code *ec) {
|
||||
if (p.empty())
|
||||
return __canonical("", ec);
|
||||
|
||||
auto abs_base = base.is_absolute() ? base : absolute(base);
|
||||
path result;
|
||||
path tmp;
|
||||
tmp.__reserve(p.native().size());
|
||||
auto PP = PathParser::CreateEnd(p.native());
|
||||
--PP;
|
||||
std::vector<string_view_t> DNEParts;
|
||||
|
||||
/* !has_root_name && !has_root_dir */
|
||||
if (root_name.empty() && root_dir.empty())
|
||||
{
|
||||
return abs_base / p;
|
||||
while (PP.State != PathParser::PS_BeforeBegin) {
|
||||
tmp.assign(createView(p.native().data(), &PP.RawEntry.back()));
|
||||
std::error_code m_ec;
|
||||
file_status st = __status(tmp, &m_ec);
|
||||
if (!status_known(st)) {
|
||||
set_or_throw(m_ec, ec, "weakly_canonical", p);
|
||||
return {};
|
||||
} else if (exists(st)) {
|
||||
result = __canonical(tmp, ec);
|
||||
break;
|
||||
}
|
||||
else if (!root_name.empty()) /* has_root_name && !has_root_dir */
|
||||
{
|
||||
return root_name / abs_base.root_directory()
|
||||
/
|
||||
abs_base.relative_path() / p.relative_path();
|
||||
}
|
||||
else /* !has_root_name && has_root_dir */
|
||||
{
|
||||
if (abs_base.has_root_name())
|
||||
return abs_base.root_name() / p;
|
||||
// else p is absolute, return outside of block
|
||||
}
|
||||
return p;
|
||||
DNEParts.push_back(*PP);
|
||||
--PP;
|
||||
}
|
||||
if (PP.State == PathParser::PS_BeforeBegin)
|
||||
result = __canonical("", ec);
|
||||
if (ec) ec->clear();
|
||||
if (DNEParts.empty())
|
||||
return result;
|
||||
for (auto It=DNEParts.rbegin(); It != DNEParts.rend(); ++It)
|
||||
result /= *It;
|
||||
return result.lexically_normal();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// path definitions
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
constexpr path::value_type path::preferred_separator;
|
||||
|
||||
path & path::replace_extension(path const & replacement)
|
||||
{
|
||||
path p = extension();
|
||||
if (not p.empty()) {
|
||||
__pn_.erase(__pn_.size() - p.native().size());
|
||||
}
|
||||
if (!replacement.empty()) {
|
||||
if (replacement.native()[0] != '.') {
|
||||
__pn_ += ".";
|
||||
}
|
||||
__pn_.append(replacement.__pn_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// path.decompose
|
||||
|
||||
string_view_t path::__root_name() const
|
||||
{
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
if (PP.State == PathParser::PS_InRootName)
|
||||
return *PP;
|
||||
return {};
|
||||
}
|
||||
|
||||
string_view_t path::__root_directory() const
|
||||
{
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
if (PP.State == PathParser::PS_InRootName)
|
||||
++PP;
|
||||
if (PP.State == PathParser::PS_InRootDir)
|
||||
return *PP;
|
||||
return {};
|
||||
}
|
||||
|
||||
string_view_t path::__root_path_raw() const
|
||||
{
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
if (PP.State == PathParser::PS_InRootName) {
|
||||
auto NextCh = PP.peek();
|
||||
if (NextCh && *NextCh == '/') {
|
||||
++PP;
|
||||
return createView(__pn_.data(), &PP.RawEntry.back());
|
||||
}
|
||||
return PP.RawEntry;
|
||||
}
|
||||
if (PP.State == PathParser::PS_InRootDir)
|
||||
return *PP;
|
||||
return {};
|
||||
}
|
||||
|
||||
static bool ConsumeRootDir(PathParser* PP) {
|
||||
while (PP->State <= PathParser::PS_InRootDir)
|
||||
++(*PP);
|
||||
return PP->State == PathParser::PS_AtEnd;
|
||||
}
|
||||
|
||||
string_view_t path::__relative_path() const
|
||||
{
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
if (ConsumeRootDir(&PP))
|
||||
return {};
|
||||
return createView(PP.RawEntry.data(), &__pn_.back());
|
||||
}
|
||||
|
||||
string_view_t path::__parent_path() const
|
||||
{
|
||||
if (empty())
|
||||
return {};
|
||||
// Determine if we have a root path but not a relative path. In that case
|
||||
// return *this.
|
||||
{
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
if (ConsumeRootDir(&PP))
|
||||
return __pn_;
|
||||
}
|
||||
// Otherwise remove a single element from the end of the path, and return
|
||||
// a string representing that path
|
||||
{
|
||||
auto PP = PathParser::CreateEnd(__pn_);
|
||||
--PP;
|
||||
if (PP.RawEntry.data() == __pn_.data())
|
||||
return {};
|
||||
--PP;
|
||||
return createView(__pn_.data(), &PP.RawEntry.back());
|
||||
}
|
||||
}
|
||||
|
||||
string_view_t path::__filename() const
|
||||
{
|
||||
if (empty()) return {};
|
||||
{
|
||||
PathParser PP = PathParser::CreateBegin(__pn_);
|
||||
if (ConsumeRootDir(&PP))
|
||||
return {};
|
||||
}
|
||||
return *(--PathParser::CreateEnd(__pn_));
|
||||
}
|
||||
|
||||
string_view_t path::__stem() const
|
||||
{
|
||||
return parser::separate_filename(__filename()).first;
|
||||
}
|
||||
|
||||
string_view_t path::__extension() const
|
||||
{
|
||||
return parser::separate_filename(__filename()).second;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// path.gen
|
||||
|
||||
|
||||
enum PathPartKind : unsigned char {
|
||||
PK_None,
|
||||
PK_RootSep,
|
||||
PK_Filename,
|
||||
PK_Dot,
|
||||
PK_DotDot,
|
||||
PK_TrailingSep
|
||||
};
|
||||
|
||||
static PathPartKind ClassifyPathPart(string_view_t Part) {
|
||||
if (Part.empty())
|
||||
return PK_TrailingSep;
|
||||
if (Part == ".")
|
||||
return PK_Dot;
|
||||
if (Part == "..")
|
||||
return PK_DotDot;
|
||||
if (Part == "/")
|
||||
return PK_RootSep;
|
||||
return PK_Filename;
|
||||
}
|
||||
|
||||
path path::lexically_normal() const {
|
||||
if (__pn_.empty())
|
||||
return *this;
|
||||
|
||||
using PartKindPair = std::pair<string_view_t, PathPartKind>;
|
||||
std::vector<PartKindPair> Parts;
|
||||
// Guess as to how many elements the path has to avoid reallocating.
|
||||
Parts.reserve(32);
|
||||
|
||||
// Track the total size of the parts as we collect them. This allows the
|
||||
// resulting path to reserve the correct amount of memory.
|
||||
size_t NewPathSize = 0;
|
||||
auto AddPart = [&](PathPartKind K, string_view_t P) {
|
||||
NewPathSize += P.size();
|
||||
Parts.emplace_back(P, K);
|
||||
};
|
||||
auto LastPartKind = [&]() {
|
||||
if (Parts.empty())
|
||||
return PK_None;
|
||||
return Parts.back().second;
|
||||
};
|
||||
|
||||
bool MaybeNeedTrailingSep = false;
|
||||
// Build a stack containing the remaining elements of the path, popping off
|
||||
// elements which occur before a '..' entry.
|
||||
for (auto PP = PathParser::CreateBegin(__pn_); PP; ++PP) {
|
||||
auto Part = *PP;
|
||||
PathPartKind Kind = ClassifyPathPart(Part);
|
||||
switch (Kind) {
|
||||
case PK_Filename:
|
||||
case PK_RootSep: {
|
||||
// Add all non-dot and non-dot-dot elements to the stack of elements.
|
||||
AddPart(Kind, Part);
|
||||
MaybeNeedTrailingSep = false;
|
||||
break;
|
||||
}
|
||||
case PK_DotDot: {
|
||||
// Only push a ".." element if there are no elements preceding the "..",
|
||||
// or if the preceding element is itself "..".
|
||||
auto LastKind = LastPartKind();
|
||||
if (LastKind == PK_Filename) {
|
||||
NewPathSize -= Parts.back().first.size();
|
||||
Parts.pop_back();
|
||||
} else if (LastKind != PK_RootSep)
|
||||
AddPart(PK_DotDot, "..");
|
||||
MaybeNeedTrailingSep = LastKind == PK_Filename;
|
||||
break;
|
||||
}
|
||||
case PK_Dot:
|
||||
case PK_TrailingSep: {
|
||||
MaybeNeedTrailingSep = true;
|
||||
break;
|
||||
}
|
||||
case PK_None:
|
||||
_LIBCPP_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
// [fs.path.generic]p6.8: If the path is empty, add a dot.
|
||||
if (Parts.empty())
|
||||
return ".";
|
||||
|
||||
// [fs.path.generic]p6.7: If the last filename is dot-dot, remove any
|
||||
// trailing directory-separator.
|
||||
bool NeedTrailingSep = MaybeNeedTrailingSep && LastPartKind() == PK_Filename;
|
||||
|
||||
path Result;
|
||||
Result.__pn_.reserve(Parts.size() + NewPathSize + NeedTrailingSep);
|
||||
for (auto &PK : Parts)
|
||||
Result /= PK.first;
|
||||
|
||||
if (NeedTrailingSep)
|
||||
Result /= "";
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static int DetermineLexicalElementCount(PathParser PP) {
|
||||
int Count = 0;
|
||||
for (; PP; ++PP) {
|
||||
auto Elem = *PP;
|
||||
if (Elem == "..")
|
||||
--Count;
|
||||
else if (Elem != ".")
|
||||
++Count;
|
||||
}
|
||||
return Count;
|
||||
}
|
||||
|
||||
path path::lexically_relative(const path& base) const {
|
||||
{ // perform root-name/root-directory mismatch checks
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
auto PPBase = PathParser::CreateBegin(base.__pn_);
|
||||
auto CheckIterMismatchAtBase = [&]() {
|
||||
return PP.State != PPBase.State && (
|
||||
PP.inRootPath() || PPBase.inRootPath());
|
||||
};
|
||||
if (PP.State == PathParser::PS_InRootName &&
|
||||
PPBase.State == PathParser::PS_InRootName) {
|
||||
if (*PP != *PPBase)
|
||||
return {};
|
||||
} else if (CheckIterMismatchAtBase())
|
||||
return {};
|
||||
|
||||
if (PP.inRootPath()) ++PP;
|
||||
if (PPBase.inRootPath()) ++PPBase;
|
||||
if (CheckIterMismatchAtBase())
|
||||
return {};
|
||||
}
|
||||
|
||||
// Find the first mismatching element
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
auto PPBase = PathParser::CreateBegin(base.__pn_);
|
||||
while (PP && PPBase && PP.State == PPBase.State &&
|
||||
*PP == *PPBase) {
|
||||
++PP;
|
||||
++PPBase;
|
||||
}
|
||||
|
||||
// If there is no mismatch, return ".".
|
||||
if (!PP && !PPBase)
|
||||
return ".";
|
||||
|
||||
// Otherwise, determine the number of elements, 'n', which are not dot or
|
||||
// dot-dot minus the number of dot-dot elements.
|
||||
int ElemCount = DetermineLexicalElementCount(PPBase);
|
||||
if (ElemCount < 0)
|
||||
return {};
|
||||
|
||||
// return a path constructed with 'n' dot-dot elements, followed by the the
|
||||
// elements of '*this' after the mismatch.
|
||||
path Result;
|
||||
// FIXME: Reserve enough room in Result that it won't have to re-allocate.
|
||||
while (ElemCount--)
|
||||
Result /= "..";
|
||||
for (; PP; ++PP)
|
||||
Result /= *PP;
|
||||
return Result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// path.comparisons
|
||||
int path::__compare(string_view_t __s) const {
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
auto PP2 = PathParser::CreateBegin(__s);
|
||||
while (PP && PP2) {
|
||||
int res = (*PP).compare(*PP2);
|
||||
if (res != 0) return res;
|
||||
++PP; ++PP2;
|
||||
}
|
||||
if (PP.State == PP2.State && !PP)
|
||||
return 0;
|
||||
if (!PP)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// path.nonmembers
|
||||
size_t hash_value(const path& __p) noexcept {
|
||||
auto PP = PathParser::CreateBegin(__p.native());
|
||||
size_t hash_value = 0;
|
||||
std::hash<string_view_t> hasher;
|
||||
while (PP) {
|
||||
hash_value = __hash_combine(hash_value, hasher(*PP));
|
||||
++PP;
|
||||
}
|
||||
return hash_value;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// path.itr
|
||||
path::iterator path::begin() const
|
||||
{
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
iterator it;
|
||||
it.__path_ptr_ = this;
|
||||
it.__state_ = PP.State;
|
||||
it.__entry_ = PP.RawEntry;
|
||||
it.__stashed_elem_.__assign_view(*PP);
|
||||
return it;
|
||||
}
|
||||
|
||||
path::iterator path::end() const
|
||||
{
|
||||
iterator it{};
|
||||
it.__state_ = PathParser::PS_AtEnd;
|
||||
it.__path_ptr_ = this;
|
||||
return it;
|
||||
}
|
||||
|
||||
path::iterator& path::iterator::__increment() {
|
||||
static_assert(__at_end == PathParser::PS_AtEnd, "");
|
||||
PathParser PP(__path_ptr_->native(), __entry_, __state_);
|
||||
++PP;
|
||||
__state_ = PP.State;
|
||||
__entry_ = PP.RawEntry;
|
||||
__stashed_elem_.__assign_view(*PP);
|
||||
return *this;
|
||||
}
|
||||
|
||||
path::iterator& path::iterator::__decrement() {
|
||||
PathParser PP(__path_ptr_->native(), __entry_, __state_);
|
||||
--PP;
|
||||
__state_ = PP.State;
|
||||
__entry_ = PP.RawEntry;
|
||||
__stashed_elem_.__assign_view(*PP);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM
|
||||
|
||||
@@ -1,448 +0,0 @@
|
||||
//===--------------------- filesystem/path.cpp ----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "experimental/filesystem"
|
||||
#include "string_view"
|
||||
#include "utility"
|
||||
|
||||
namespace { namespace parser
|
||||
{
|
||||
using namespace std;
|
||||
using namespace std::experimental::filesystem;
|
||||
|
||||
using string_view_t = path::__string_view;
|
||||
using string_view_pair = pair<string_view_t, string_view_t>;
|
||||
using PosPtr = path::value_type const*;
|
||||
|
||||
struct PathParser {
|
||||
enum ParserState : unsigned char {
|
||||
// Zero is a special sentinel value used by default constructed iterators.
|
||||
PS_BeforeBegin = 1,
|
||||
PS_InRootName,
|
||||
PS_InRootDir,
|
||||
PS_InFilenames,
|
||||
PS_InTrailingSep,
|
||||
PS_AtEnd
|
||||
};
|
||||
|
||||
const string_view_t Path;
|
||||
string_view_t RawEntry;
|
||||
ParserState State;
|
||||
|
||||
private:
|
||||
PathParser(string_view_t P, ParserState State) noexcept
|
||||
: Path(P), State(State) {}
|
||||
|
||||
public:
|
||||
PathParser(string_view_t P, string_view_t E, unsigned char S)
|
||||
: Path(P), RawEntry(E), State(static_cast<ParserState>(S)) {
|
||||
// S cannot be '0' or PS_BeforeBegin.
|
||||
}
|
||||
|
||||
static PathParser CreateBegin(string_view_t P) noexcept {
|
||||
PathParser PP(P, PS_BeforeBegin);
|
||||
PP.increment();
|
||||
return PP;
|
||||
}
|
||||
|
||||
static PathParser CreateEnd(string_view_t P) noexcept {
|
||||
PathParser PP(P, PS_AtEnd);
|
||||
return PP;
|
||||
}
|
||||
|
||||
PosPtr peek() const noexcept {
|
||||
auto TkEnd = getNextTokenStartPos();
|
||||
auto End = getAfterBack();
|
||||
return TkEnd == End ? nullptr : TkEnd;
|
||||
}
|
||||
|
||||
void increment() noexcept {
|
||||
const PosPtr End = getAfterBack();
|
||||
const PosPtr Start = getNextTokenStartPos();
|
||||
if (Start == End)
|
||||
return makeState(PS_AtEnd);
|
||||
|
||||
switch (State) {
|
||||
case PS_BeforeBegin: {
|
||||
PosPtr TkEnd = consumeSeparator(Start, End);
|
||||
// If we consumed exactly two separators we have a root name.
|
||||
if (TkEnd && TkEnd == Start + 2) {
|
||||
// FIXME Do we need to consume a name or is '//' a root name on its own?
|
||||
// what about '//.', '//..', '//...'?
|
||||
auto NameEnd = consumeName(TkEnd, End);
|
||||
if (NameEnd)
|
||||
TkEnd = NameEnd;
|
||||
return makeState(PS_InRootName, Start, TkEnd);
|
||||
}
|
||||
else if (TkEnd)
|
||||
return makeState(PS_InRootDir, Start, TkEnd);
|
||||
else
|
||||
return makeState(PS_InFilenames, Start, consumeName(Start, End));
|
||||
}
|
||||
|
||||
case PS_InRootName:
|
||||
return makeState(PS_InRootDir, Start, consumeSeparator(Start, End));
|
||||
case PS_InRootDir:
|
||||
return makeState(PS_InFilenames, Start, consumeName(Start, End));
|
||||
|
||||
case PS_InFilenames: {
|
||||
PosPtr SepEnd = consumeSeparator(Start, End);
|
||||
if (SepEnd != End) {
|
||||
PosPtr TkEnd = consumeName(SepEnd, End);
|
||||
if (TkEnd)
|
||||
return makeState(PS_InFilenames, SepEnd, TkEnd);
|
||||
}
|
||||
return makeState(PS_InTrailingSep, Start, SepEnd);
|
||||
}
|
||||
|
||||
case PS_InTrailingSep:
|
||||
return makeState(PS_AtEnd);
|
||||
|
||||
case PS_AtEnd:
|
||||
_LIBCPP_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void decrement() noexcept {
|
||||
const PosPtr REnd = getBeforeFront();
|
||||
const PosPtr RStart = getCurrentTokenStartPos() - 1;
|
||||
|
||||
switch (State) {
|
||||
case PS_AtEnd: {
|
||||
// Try to consume a trailing separator or root directory first.
|
||||
if (PosPtr SepEnd = consumeSeparator(RStart, REnd)) {
|
||||
if (SepEnd == REnd)
|
||||
return makeState((RStart == REnd + 2) ? PS_InRootName : PS_InRootDir,
|
||||
Path.data(), RStart + 1);
|
||||
// Check if we're seeing the root directory separator
|
||||
auto PP = CreateBegin(Path);
|
||||
bool InRootDir = PP.State == PS_InRootName &&
|
||||
&PP.RawEntry.back() == SepEnd;
|
||||
return makeState(InRootDir ? PS_InRootDir : PS_InTrailingSep,
|
||||
SepEnd + 1, RStart + 1);
|
||||
} else {
|
||||
PosPtr TkStart = consumeName(RStart, REnd);
|
||||
if (TkStart == REnd + 2 && consumeSeparator(TkStart, REnd) == REnd)
|
||||
return makeState(PS_InRootName, Path.data(), RStart + 1);
|
||||
else
|
||||
return makeState(PS_InFilenames, TkStart + 1, RStart + 1);
|
||||
}
|
||||
}
|
||||
case PS_InTrailingSep:
|
||||
return makeState(PS_InFilenames, consumeName(RStart, REnd) + 1, RStart + 1);
|
||||
case PS_InFilenames: {
|
||||
PosPtr SepEnd = consumeSeparator(RStart, REnd);
|
||||
if (SepEnd == REnd)
|
||||
return makeState((RStart == REnd + 2) ? PS_InRootName : PS_InRootDir,
|
||||
Path.data(), RStart + 1);
|
||||
PosPtr TkEnd = consumeName(SepEnd, REnd);
|
||||
if (TkEnd == REnd + 2 && consumeSeparator(TkEnd, REnd) == REnd)
|
||||
return makeState(PS_InRootDir, SepEnd + 1, RStart + 1);
|
||||
return makeState(PS_InFilenames, TkEnd + 1, SepEnd + 1);
|
||||
}
|
||||
case PS_InRootDir:
|
||||
return makeState(PS_InRootName, Path.data(), RStart + 1);
|
||||
case PS_InRootName:
|
||||
case PS_BeforeBegin:
|
||||
_LIBCPP_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Return a view with the "preferred representation" of the current
|
||||
/// element. For example trailing separators are represented as a '.'
|
||||
string_view_t operator*() const noexcept {
|
||||
switch (State) {
|
||||
case PS_BeforeBegin:
|
||||
case PS_AtEnd:
|
||||
return "";
|
||||
case PS_InRootDir:
|
||||
return "/";
|
||||
case PS_InTrailingSep:
|
||||
return ".";
|
||||
case PS_InRootName:
|
||||
case PS_InFilenames:
|
||||
return RawEntry;
|
||||
}
|
||||
_LIBCPP_UNREACHABLE();
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return State != PS_BeforeBegin && State != PS_AtEnd;
|
||||
}
|
||||
|
||||
PathParser& operator++() noexcept {
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
PathParser& operator--() noexcept {
|
||||
decrement();
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
void makeState(ParserState NewState, PosPtr Start, PosPtr End) noexcept {
|
||||
State = NewState;
|
||||
RawEntry = string_view_t(Start, End - Start);
|
||||
}
|
||||
void makeState(ParserState NewState) noexcept {
|
||||
State = NewState;
|
||||
RawEntry = {};
|
||||
}
|
||||
|
||||
PosPtr getAfterBack() const noexcept {
|
||||
return Path.data() + Path.size();
|
||||
}
|
||||
|
||||
PosPtr getBeforeFront() const noexcept {
|
||||
return Path.data() - 1;
|
||||
}
|
||||
|
||||
/// \brief Return a pointer to the first character after the currently
|
||||
/// lexed element.
|
||||
PosPtr getNextTokenStartPos() const noexcept {
|
||||
switch (State) {
|
||||
case PS_BeforeBegin:
|
||||
return Path.data();
|
||||
case PS_InRootName:
|
||||
case PS_InRootDir:
|
||||
case PS_InFilenames:
|
||||
return &RawEntry.back() + 1;
|
||||
case PS_InTrailingSep:
|
||||
case PS_AtEnd:
|
||||
return getAfterBack();
|
||||
}
|
||||
_LIBCPP_UNREACHABLE();
|
||||
}
|
||||
|
||||
/// \brief Return a pointer to the first character in the currently lexed
|
||||
/// element.
|
||||
PosPtr getCurrentTokenStartPos() const noexcept {
|
||||
switch (State) {
|
||||
case PS_BeforeBegin:
|
||||
case PS_InRootName:
|
||||
return &Path.front();
|
||||
case PS_InRootDir:
|
||||
case PS_InFilenames:
|
||||
case PS_InTrailingSep:
|
||||
return &RawEntry.front();
|
||||
case PS_AtEnd:
|
||||
return &Path.back() + 1;
|
||||
}
|
||||
_LIBCPP_UNREACHABLE();
|
||||
}
|
||||
|
||||
PosPtr consumeSeparator(PosPtr P, PosPtr End) const noexcept {
|
||||
if (P == End || *P != '/')
|
||||
return nullptr;
|
||||
const int Inc = P < End ? 1 : -1;
|
||||
P += Inc;
|
||||
while (P != End && *P == '/')
|
||||
P += Inc;
|
||||
return P;
|
||||
}
|
||||
|
||||
PosPtr consumeName(PosPtr P, PosPtr End) const noexcept {
|
||||
if (P == End || *P == '/')
|
||||
return nullptr;
|
||||
const int Inc = P < End ? 1 : -1;
|
||||
P += Inc;
|
||||
while (P != End && *P != '/')
|
||||
P += Inc;
|
||||
return P;
|
||||
}
|
||||
};
|
||||
|
||||
string_view_pair separate_filename(string_view_t const & s) {
|
||||
if (s == "." || s == ".." || s.empty()) return string_view_pair{s, ""};
|
||||
auto pos = s.find_last_of('.');
|
||||
if (pos == string_view_t::npos)
|
||||
return string_view_pair{s, string_view_t{}};
|
||||
return string_view_pair{s.substr(0, pos), s.substr(pos)};
|
||||
}
|
||||
|
||||
string_view_t createView(PosPtr S, PosPtr E) noexcept {
|
||||
return {S, static_cast<size_t>(E - S) + 1};
|
||||
}
|
||||
|
||||
}} // namespace parser
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
|
||||
|
||||
using parser::string_view_t;
|
||||
using parser::string_view_pair;
|
||||
using parser::PathParser;
|
||||
using parser::createView;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// path definitions
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
constexpr path::value_type path::preferred_separator;
|
||||
|
||||
path & path::replace_extension(path const & replacement)
|
||||
{
|
||||
path p = extension();
|
||||
if (not p.empty()) {
|
||||
__pn_.erase(__pn_.size() - p.native().size());
|
||||
}
|
||||
if (!replacement.empty()) {
|
||||
if (replacement.native()[0] != '.') {
|
||||
__pn_ += ".";
|
||||
}
|
||||
__pn_.append(replacement.__pn_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// path.decompose
|
||||
|
||||
string_view_t path::__root_name() const
|
||||
{
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
if (PP.State == PathParser::PS_InRootName)
|
||||
return *PP;
|
||||
return {};
|
||||
}
|
||||
|
||||
string_view_t path::__root_directory() const
|
||||
{
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
if (PP.State == PathParser::PS_InRootName)
|
||||
++PP;
|
||||
if (PP.State == PathParser::PS_InRootDir)
|
||||
return *PP;
|
||||
return {};
|
||||
}
|
||||
|
||||
string_view_t path::__root_path_raw() const
|
||||
{
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
if (PP.State == PathParser::PS_InRootName) {
|
||||
auto NextCh = PP.peek();
|
||||
if (NextCh && *NextCh == '/') {
|
||||
++PP;
|
||||
return createView(__pn_.data(), &PP.RawEntry.back());
|
||||
}
|
||||
return PP.RawEntry;
|
||||
}
|
||||
if (PP.State == PathParser::PS_InRootDir)
|
||||
return *PP;
|
||||
return {};
|
||||
}
|
||||
|
||||
string_view_t path::__relative_path() const
|
||||
{
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
while (PP.State <= PathParser::PS_InRootDir)
|
||||
++PP;
|
||||
if (PP.State == PathParser::PS_AtEnd)
|
||||
return {};
|
||||
return createView(PP.RawEntry.data(), &__pn_.back());
|
||||
}
|
||||
|
||||
string_view_t path::__parent_path() const
|
||||
{
|
||||
if (empty())
|
||||
return {};
|
||||
auto PP = PathParser::CreateEnd(__pn_);
|
||||
--PP;
|
||||
if (PP.RawEntry.data() == __pn_.data())
|
||||
return {};
|
||||
--PP;
|
||||
return createView(__pn_.data(), &PP.RawEntry.back());
|
||||
}
|
||||
|
||||
string_view_t path::__filename() const
|
||||
{
|
||||
if (empty()) return {};
|
||||
return *(--PathParser::CreateEnd(__pn_));
|
||||
}
|
||||
|
||||
string_view_t path::__stem() const
|
||||
{
|
||||
return parser::separate_filename(__filename()).first;
|
||||
}
|
||||
|
||||
string_view_t path::__extension() const
|
||||
{
|
||||
return parser::separate_filename(__filename()).second;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// path.comparisons
|
||||
int path::__compare(string_view_t __s) const {
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
auto PP2 = PathParser::CreateBegin(__s);
|
||||
while (PP && PP2) {
|
||||
int res = (*PP).compare(*PP2);
|
||||
if (res != 0) return res;
|
||||
++PP; ++PP2;
|
||||
}
|
||||
if (PP.State == PP2.State && PP.State == PathParser::PS_AtEnd)
|
||||
return 0;
|
||||
if (PP.State == PathParser::PS_AtEnd)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// path.nonmembers
|
||||
size_t hash_value(const path& __p) noexcept {
|
||||
auto PP = PathParser::CreateBegin(__p.native());
|
||||
size_t hash_value = 0;
|
||||
std::hash<string_view_t> hasher;
|
||||
while (PP) {
|
||||
hash_value = __hash_combine(hash_value, hasher(*PP));
|
||||
++PP;
|
||||
}
|
||||
return hash_value;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// path.itr
|
||||
path::iterator path::begin() const
|
||||
{
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
iterator it;
|
||||
it.__path_ptr_ = this;
|
||||
it.__state_ = PP.State;
|
||||
it.__entry_ = PP.RawEntry;
|
||||
it.__stashed_elem_.__assign_view(*PP);
|
||||
return it;
|
||||
}
|
||||
|
||||
path::iterator path::end() const
|
||||
{
|
||||
iterator it{};
|
||||
it.__state_ = PathParser::PS_AtEnd;
|
||||
it.__path_ptr_ = this;
|
||||
return it;
|
||||
}
|
||||
|
||||
path::iterator& path::iterator::__increment() {
|
||||
static_assert(__at_end == PathParser::PS_AtEnd, "");
|
||||
PathParser PP(__path_ptr_->native(), __entry_, __state_);
|
||||
++PP;
|
||||
__state_ = PP.State;
|
||||
__entry_ = PP.RawEntry;
|
||||
__stashed_elem_.__assign_view(*PP);
|
||||
return *this;
|
||||
}
|
||||
|
||||
path::iterator& path::iterator::__decrement() {
|
||||
PathParser PP(__path_ptr_->native(), __entry_, __state_);
|
||||
--PP;
|
||||
__state_ = PP.State;
|
||||
__entry_ = PP.RawEntry;
|
||||
__stashed_elem_.__assign_view(*PP);
|
||||
return *this;
|
||||
}
|
||||
|
||||
_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM
|
||||
@@ -1,70 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03
|
||||
|
||||
// <experimental/filesystem>
|
||||
|
||||
// class path
|
||||
|
||||
// path& operator/=(path const&)
|
||||
// path operator/(path const&, path const&)
|
||||
|
||||
|
||||
#define _LIBCPP_DEBUG 0
|
||||
#define _LIBCPP_ASSERT(x, m) ((x) ? (void)0 : (void)::AssertCount++)
|
||||
int AssertCount = 0;
|
||||
|
||||
#include <experimental/filesystem>
|
||||
#include <type_traits>
|
||||
#include <string_view>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "test_iterators.h"
|
||||
#include "count_new.hpp"
|
||||
#include "filesystem_test_helper.hpp"
|
||||
|
||||
namespace fs = std::experimental::filesystem;
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace fs;
|
||||
{
|
||||
path lhs("//foo");
|
||||
path rhs("/bar");
|
||||
assert(AssertCount == 0);
|
||||
lhs /= rhs;
|
||||
assert(AssertCount == 0);
|
||||
}
|
||||
{
|
||||
path lhs("//foo");
|
||||
path rhs("/bar");
|
||||
assert(AssertCount == 0);
|
||||
(void)(lhs / rhs);
|
||||
assert(AssertCount == 0);
|
||||
}
|
||||
{
|
||||
path lhs("//foo");
|
||||
path rhs("//bar");
|
||||
assert(AssertCount == 0);
|
||||
lhs /= rhs;
|
||||
assert(AssertCount == 1);
|
||||
AssertCount = 0;
|
||||
}
|
||||
{
|
||||
path lhs("//foo");
|
||||
path rhs("//bar");
|
||||
assert(AssertCount == 0);
|
||||
(void)(lhs / rhs);
|
||||
assert(AssertCount == 1);
|
||||
}
|
||||
// FIXME The same error is not diagnosed for the append(Source) and
|
||||
// append(It, It) overloads.
|
||||
}
|
||||
@@ -83,14 +83,14 @@ void checkBeginEndBasic() {
|
||||
}
|
||||
{
|
||||
path p("//root_name//first_dir////second_dir");
|
||||
const path expect[] = {"//root_name", "/", "first_dir", "second_dir"};
|
||||
const path expect[] = {"/", "root_name", "first_dir", "second_dir"};
|
||||
assert(checkCollectionsEqual(p.begin(), p.end(), std::begin(expect), std::end(expect)));
|
||||
assert(checkCollectionsEqualBackwards(p.begin(), p.end(), std::begin(expect), std::end(expect)));
|
||||
|
||||
}
|
||||
{
|
||||
path p("////foo/bar/baz///");
|
||||
const path expect[] = {"/", "foo", "bar", "baz", "."};
|
||||
const path expect[] = {"/", "foo", "bar", "baz", ""};
|
||||
assert(checkCollectionsEqual(p.begin(), p.end(), std::begin(expect), std::end(expect)));
|
||||
assert(checkCollectionsEqualBackwards(p.begin(), p.end(), std::begin(expect), std::end(expect)));
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "test_iterators.h"
|
||||
#include "count_new.hpp"
|
||||
#include "filesystem_test_helper.hpp"
|
||||
#include "verbose_assert.h"
|
||||
|
||||
|
||||
struct AppendOperatorTestcase {
|
||||
@@ -45,13 +46,22 @@ const AppendOperatorTestcase Cases[] =
|
||||
{S(""), S(""), S("")}
|
||||
, {S("p1"), S("p2"), S("p1/p2")}
|
||||
, {S("p1/"), S("p2"), S("p1/p2")}
|
||||
, {S("p1"), S("/p2"), S("p1/p2")}
|
||||
, {S("p1/"), S("/p2"), S("p1//p2")}
|
||||
, {S("p1"), S("/p2"), S("/p2")}
|
||||
, {S("p1/"), S("/p2"), S("/p2")}
|
||||
, {S("p1"), S("\\p2"), S("p1/\\p2")}
|
||||
, {S("p1\\"), S("p2"), S("p1\\/p2")}
|
||||
, {S("p1\\"), S("\\p2"), S("p1\\/\\p2")}
|
||||
, {S("p1"), S(""), S("p1")}
|
||||
, {S(""), S("p2"), S("p2")}
|
||||
, {S("/p1"), S("p2"), S("/p1/p2")}
|
||||
, {S("/p1"), S("/p2"), S("/p2")}
|
||||
, {S("/p1/p3"), S("p2"), S("/p1/p3/p2")}
|
||||
, {S("/p1/p3/"), S("p2"), S("/p1/p3/p2")}
|
||||
, {S("/p1/"), S("p2"), S("/p1/p2")}
|
||||
, {S("/p1/p3/"), S("/p2/p4"), S("/p2/p4")}
|
||||
, {S("/"), S(""), S("/")}
|
||||
, {S("/p1"), S("/p2/"), S("/p2/")}
|
||||
, {S("p1"), S(""), S("p1/")}
|
||||
, {S("p1/"), S(""), S("p1/")}
|
||||
};
|
||||
|
||||
|
||||
@@ -59,7 +69,8 @@ const AppendOperatorTestcase LongLHSCases[] =
|
||||
{
|
||||
{S("p1"), S("p2"), S("p1/p2")}
|
||||
, {S("p1/"), S("p2"), S("p1/p2")}
|
||||
, {S("p1"), S("/p2"), S("p1/p2")}
|
||||
, {S("p1"), S("/p2"), S("/p2")}
|
||||
, {S("/p1"), S("p2"), S("/p1/p2")}
|
||||
};
|
||||
#undef S
|
||||
|
||||
@@ -98,7 +109,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC)
|
||||
DisableAllocationGuard g;
|
||||
LHS /= RHS;
|
||||
}
|
||||
assert(LHS == E);
|
||||
ASSERT_PRED(PathEq, LHS , E);
|
||||
}
|
||||
// basic_string_view
|
||||
{
|
||||
@@ -108,7 +119,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC)
|
||||
DisableAllocationGuard g;
|
||||
LHS /= RHS;
|
||||
}
|
||||
assert(LHS == E);
|
||||
assert(PathEq(LHS, E));
|
||||
}
|
||||
// CharT*
|
||||
{
|
||||
@@ -118,7 +129,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC)
|
||||
DisableAllocationGuard g;
|
||||
LHS /= RHS;
|
||||
}
|
||||
assert(LHS == E);
|
||||
assert(PathEq(LHS, E));
|
||||
}
|
||||
{
|
||||
path LHS(L); PathReserve(LHS, ReserveSize);
|
||||
@@ -127,7 +138,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC)
|
||||
DisableAllocationGuard g;
|
||||
LHS.append(RHS, StrEnd(RHS));
|
||||
}
|
||||
assert(LHS == E);
|
||||
assert(PathEq(LHS, E));
|
||||
}
|
||||
// input iterator - For non-native char types, appends needs to copy the
|
||||
// iterator range into a contiguous block of memory before it can perform the
|
||||
@@ -143,7 +154,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC)
|
||||
if (DisableAllocations) g.requireExactly(0);
|
||||
LHS /= RHS;
|
||||
}
|
||||
assert(LHS == E);
|
||||
assert(PathEq(LHS, E));
|
||||
}
|
||||
{
|
||||
path LHS(L); PathReserve(LHS, ReserveSize);
|
||||
@@ -154,7 +165,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC)
|
||||
if (DisableAllocations) g.requireExactly(0);
|
||||
LHS.append(RHS, REnd);
|
||||
}
|
||||
assert(LHS == E);
|
||||
assert(PathEq(LHS, E));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,17 +182,18 @@ void doAppendSourceTest(AppendOperatorTestcase const& TC)
|
||||
const Ptr E = TC.expect;
|
||||
// basic_string
|
||||
{
|
||||
path LHS(L);
|
||||
path Result(L);
|
||||
Str RHS(R);
|
||||
path& Ref = (LHS /= RHS);
|
||||
assert(LHS == E);
|
||||
assert(&Ref == &LHS);
|
||||
path& Ref = (Result /= RHS);
|
||||
ASSERT_EQ(Result, E)
|
||||
<< DISPLAY(L) << DISPLAY(R);
|
||||
assert(&Ref == &Result);
|
||||
}
|
||||
{
|
||||
path LHS(L);
|
||||
Str RHS(R);
|
||||
path& Ref = LHS.append(RHS);
|
||||
assert(LHS == E);
|
||||
assert(PathEq(LHS, E));
|
||||
assert(&Ref == &LHS);
|
||||
}
|
||||
// basic_string_view
|
||||
@@ -189,14 +201,14 @@ void doAppendSourceTest(AppendOperatorTestcase const& TC)
|
||||
path LHS(L);
|
||||
StrView RHS(R);
|
||||
path& Ref = (LHS /= RHS);
|
||||
assert(LHS == E);
|
||||
assert(PathEq(LHS, E));
|
||||
assert(&Ref == &LHS);
|
||||
}
|
||||
{
|
||||
path LHS(L);
|
||||
StrView RHS(R);
|
||||
path& Ref = LHS.append(RHS);
|
||||
assert(LHS == E);
|
||||
assert(PathEq(LHS, E));
|
||||
assert(&Ref == &LHS);
|
||||
}
|
||||
// Char*
|
||||
@@ -204,21 +216,22 @@ void doAppendSourceTest(AppendOperatorTestcase const& TC)
|
||||
path LHS(L);
|
||||
Str RHS(R);
|
||||
path& Ref = (LHS /= RHS);
|
||||
assert(LHS == E);
|
||||
assert(PathEq(LHS, E));
|
||||
assert(&Ref == &LHS);
|
||||
}
|
||||
{
|
||||
path LHS(L);
|
||||
Ptr RHS(R);
|
||||
path& Ref = LHS.append(RHS);
|
||||
assert(LHS == E);
|
||||
assert(PathEq(LHS, E));
|
||||
assert(&Ref == &LHS);
|
||||
}
|
||||
{
|
||||
path LHS(L);
|
||||
Ptr RHS(R);
|
||||
path& Ref = LHS.append(RHS, StrEnd(RHS));
|
||||
assert(LHS == E);
|
||||
ASSERT_PRED(PathEq, LHS, E)
|
||||
<< DISPLAY(L) << DISPLAY(R);
|
||||
assert(&Ref == &LHS);
|
||||
}
|
||||
// iterators
|
||||
@@ -226,13 +239,13 @@ void doAppendSourceTest(AppendOperatorTestcase const& TC)
|
||||
path LHS(L);
|
||||
InputIter RHS(R);
|
||||
path& Ref = (LHS /= RHS);
|
||||
assert(LHS == E);
|
||||
assert(PathEq(LHS, E));
|
||||
assert(&Ref == &LHS);
|
||||
}
|
||||
{
|
||||
path LHS(L); InputIter RHS(R);
|
||||
path& Ref = LHS.append(RHS);
|
||||
assert(LHS == E);
|
||||
assert(PathEq(LHS, E));
|
||||
assert(&Ref == &LHS);
|
||||
}
|
||||
{
|
||||
@@ -240,7 +253,7 @@ void doAppendSourceTest(AppendOperatorTestcase const& TC)
|
||||
InputIter RHS(R);
|
||||
InputIter REnd(StrEnd(R));
|
||||
path& Ref = LHS.append(RHS, REnd);
|
||||
assert(LHS == E);
|
||||
assert(PathEq(LHS, E));
|
||||
assert(&Ref == &LHS);
|
||||
}
|
||||
}
|
||||
@@ -304,11 +317,14 @@ int main()
|
||||
using namespace fs;
|
||||
for (auto const & TC : Cases) {
|
||||
{
|
||||
path LHS((const char*)TC.lhs);
|
||||
path RHS((const char*)TC.rhs);
|
||||
path& Ref = (LHS /= RHS);
|
||||
assert(LHS == (const char*)TC.expect);
|
||||
assert(&Ref == &LHS);
|
||||
const char* LHS_In = TC.lhs;
|
||||
const char* RHS_In = TC.rhs;
|
||||
path LHS(LHS_In);
|
||||
path RHS(RHS_In);
|
||||
path& Res = (LHS /= RHS);
|
||||
ASSERT_PRED(PathEq, Res, (const char*)TC.expect)
|
||||
<< DISPLAY(LHS_In) << DISPLAY(RHS_In);
|
||||
assert(&Res == &LHS);
|
||||
}
|
||||
doAppendSourceTest<char> (TC);
|
||||
doAppendSourceTest<wchar_t> (TC);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
//
|
||||
// size_t hash_value(path const&) noexcept;
|
||||
|
||||
|
||||
#include "filesystem_include.hpp"
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
@@ -35,7 +36,7 @@
|
||||
#include "test_iterators.h"
|
||||
#include "count_new.hpp"
|
||||
#include "filesystem_test_helper.hpp"
|
||||
|
||||
#include "verbose_assert.h"
|
||||
|
||||
struct PathCompareTest {
|
||||
const char* LHS;
|
||||
@@ -57,8 +58,9 @@ const PathCompareTest CompareTestCases[] =
|
||||
{"a/b/c", "b/a/c", -1},
|
||||
{"a/b", "a/b/c", -1},
|
||||
{"a/b/c", "a/b", 1},
|
||||
{"a/b/", "a/b/.", 0},
|
||||
{"a/b//////", "a/b/////.", 0},
|
||||
{"a/b/", "a/b/.", -1},
|
||||
{"a/b/", "a/b", 1},
|
||||
{"a/b//////", "a/b/////.", -1},
|
||||
{"a/.././b", "a///..//.////b", 0},
|
||||
{"//foo//bar///baz////", "//foo/bar/baz/", 0}, // duplicate separators
|
||||
{"///foo/bar", "/foo/bar", 0}, // "///" is not a root directory
|
||||
@@ -94,8 +96,13 @@ int main()
|
||||
int ret2 = normalize_ret(p1.compare(R));
|
||||
int ret3 = normalize_ret(p1.compare(TC.RHS));
|
||||
int ret4 = normalize_ret(p1.compare(RV));
|
||||
assert(ret1 == ret2 && ret1 == ret3 && ret1 == ret4);
|
||||
assert(ret1 == E);
|
||||
|
||||
g.release();
|
||||
ASSERT_EQ(ret1, ret2);
|
||||
ASSERT_EQ(ret1, ret3);
|
||||
ASSERT_EQ(ret1, ret4);
|
||||
ASSERT_EQ(ret1, E)
|
||||
<< DISPLAY(TC.LHS) << DISPLAY(TC.RHS);
|
||||
|
||||
// check signatures
|
||||
ASSERT_NOEXCEPT(p1.compare(p2));
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
// UNSUPPORTED: clang-3.3, clang-3.4, clang-3.5, clang-3.6, clang-3.7, clang-3.8
|
||||
|
||||
#include "filesystem_include.hpp"
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main ()
|
||||
|
||||
@@ -53,6 +53,14 @@
|
||||
#include "test_iterators.h"
|
||||
#include "count_new.hpp"
|
||||
#include "filesystem_test_helper.hpp"
|
||||
#include "assert_checkpoint.h"
|
||||
#include "verbose_assert.h"
|
||||
|
||||
struct ComparePathExact {
|
||||
bool operator()(std::string const& LHS, std::string const& RHS) const {
|
||||
return LHS == RHS;
|
||||
}
|
||||
};
|
||||
|
||||
struct PathDecomposeTestcase
|
||||
{
|
||||
@@ -72,80 +80,89 @@ const PathDecomposeTestcase PathTestCases[] =
|
||||
, {".", {"."}, "", "", "", ".", "", "."}
|
||||
, {"..", {".."}, "", "", "", "..", "", ".."}
|
||||
, {"foo", {"foo"}, "", "", "", "foo", "", "foo"}
|
||||
, {"/", {"/"}, "/", "", "/", "", "", "/"}
|
||||
, {"/", {"/"}, "/", "", "/", "", "/", ""}
|
||||
, {"/foo", {"/", "foo"}, "/", "", "/", "foo", "/", "foo"}
|
||||
, {"foo/", {"foo", "."}, "", "", "", "foo/", "foo", "."}
|
||||
, {"/foo/", {"/", "foo", "."}, "/", "", "/", "foo/", "/foo", "."}
|
||||
, {"foo/", {"foo", ""}, "", "", "", "foo/", "foo", ""}
|
||||
, {"/foo/", {"/", "foo", ""}, "/", "", "/", "foo/", "/foo", ""}
|
||||
, {"foo/bar", {"foo","bar"}, "", "", "", "foo/bar", "foo", "bar"}
|
||||
, {"/foo//bar", {"/","foo","bar"}, "/", "", "/", "foo/bar", "/foo", "bar"}
|
||||
, {"//net", {"//net"}, "//net", "//net", "", "", "", "//net"}
|
||||
, {"//net/foo", {"//net", "/", "foo"}, "//net/", "//net", "/", "foo", "//net/", "foo"}
|
||||
, {"///foo///", {"/", "foo", "."}, "/", "", "/", "foo///", "///foo", "."}
|
||||
, {"//net", {"/", "net"}, "/", "", "/", "net", "/", "net"}
|
||||
, {"//net/foo", {"/", "net", "foo"}, "/", "", "/", "net/foo", "/net", "foo"}
|
||||
, {"///foo///", {"/", "foo", ""}, "/", "", "/", "foo///", "///foo", ""}
|
||||
, {"///foo///bar", {"/", "foo", "bar"}, "/", "", "/", "foo///bar", "///foo", "bar"}
|
||||
, {"/.", {"/", "."}, "/", "", "/", ".", "/", "."}
|
||||
, {"./", {".", "."}, "", "", "", "./", ".", "."}
|
||||
, {"./", {".", ""}, "", "", "", "./", ".", ""}
|
||||
, {"/..", {"/", ".."}, "/", "", "/", "..", "/", ".."}
|
||||
, {"../", {"..", "."}, "", "", "", "../", "..", "."}
|
||||
, {"../", {"..", ""}, "", "", "", "../", "..", ""}
|
||||
, {"foo/.", {"foo", "."}, "", "", "", "foo/.", "foo", "."}
|
||||
, {"foo/..", {"foo", ".."}, "", "", "", "foo/..", "foo", ".."}
|
||||
, {"foo/./", {"foo", ".", "."}, "", "", "", "foo/./", "foo/.", "."}
|
||||
, {"foo/./", {"foo", ".", ""}, "", "", "", "foo/./", "foo/.", ""}
|
||||
, {"foo/./bar", {"foo", ".", "bar"}, "", "", "", "foo/./bar", "foo/.", "bar"}
|
||||
, {"foo/../", {"foo", "..", "."}, "", "", "", "foo/../", "foo/..", "."}
|
||||
, {"foo/../", {"foo", "..", ""}, "", "", "", "foo/../", "foo/..", ""}
|
||||
, {"foo/../bar", {"foo", "..", "bar"}, "", "", "", "foo/../bar", "foo/..", "bar"}
|
||||
, {"c:", {"c:"}, "", "", "", "c:", "", "c:"}
|
||||
, {"c:/", {"c:", "."}, "", "", "", "c:/", "c:", "."}
|
||||
, {"c:/", {"c:", ""}, "", "", "", "c:/", "c:", ""}
|
||||
, {"c:foo", {"c:foo"}, "", "", "", "c:foo", "", "c:foo"}
|
||||
, {"c:/foo", {"c:", "foo"}, "", "", "", "c:/foo", "c:", "foo"}
|
||||
, {"c:foo/", {"c:foo", "."}, "", "", "", "c:foo/", "c:foo", "."}
|
||||
, {"c:/foo/", {"c:", "foo", "."}, "", "", "", "c:/foo/", "c:/foo", "."}
|
||||
, {"c:foo/", {"c:foo", ""}, "", "", "", "c:foo/", "c:foo", ""}
|
||||
, {"c:/foo/", {"c:", "foo", ""}, "", "", "", "c:/foo/", "c:/foo", ""}
|
||||
, {"c:/foo/bar", {"c:", "foo", "bar"}, "", "", "", "c:/foo/bar", "c:/foo", "bar"}
|
||||
, {"prn:", {"prn:"}, "", "", "", "prn:", "", "prn:"}
|
||||
, {"c:\\", {"c:\\"}, "", "", "", "c:\\", "", "c:\\"}
|
||||
, {"c:\\foo", {"c:\\foo"}, "", "", "", "c:\\foo", "", "c:\\foo"}
|
||||
, {"c:foo\\", {"c:foo\\"}, "", "", "", "c:foo\\", "", "c:foo\\"}
|
||||
, {"c:\\foo\\", {"c:\\foo\\"}, "", "", "", "c:\\foo\\", "", "c:\\foo\\"}
|
||||
, {"c:\\foo/", {"c:\\foo", "."}, "", "", "", "c:\\foo/", "c:\\foo", "."}
|
||||
, {"c:\\foo/", {"c:\\foo", ""}, "", "", "", "c:\\foo/", "c:\\foo", ""}
|
||||
, {"c:/foo\\bar", {"c:", "foo\\bar"}, "", "", "", "c:/foo\\bar", "c:", "foo\\bar"}
|
||||
, {"//", {"//"}, "//", "//", "", "", "", "//"}
|
||||
, {"//", {"/"}, "/", "", "/", "", "/", ""}
|
||||
};
|
||||
|
||||
void decompPathTest()
|
||||
{
|
||||
using namespace fs;
|
||||
for (auto const & TC : PathTestCases) {
|
||||
path p(TC.raw);
|
||||
assert(p == TC.raw);
|
||||
CHECKPOINT(TC.raw.c_str());
|
||||
fs::path p(TC.raw);
|
||||
ASSERT(p == TC.raw);
|
||||
|
||||
assert(p.root_path() == TC.root_path);
|
||||
assert(p.has_root_path() != TC.root_path.empty());
|
||||
ASSERT_EQ(p.root_path(), TC.root_path);
|
||||
ASSERT_NEQ(p.has_root_path(), TC.root_path.empty());
|
||||
|
||||
assert(p.root_name() == TC.root_name);
|
||||
assert(p.has_root_name() != TC.root_name.empty());
|
||||
ASSERT(p.root_name().native().empty())
|
||||
<< DISPLAY(p.root_name());
|
||||
ASSERT_EQ(p.root_name(),TC.root_name);
|
||||
ASSERT_NEQ(p.has_root_name(), TC.root_name.empty());
|
||||
|
||||
assert(p.root_directory() == TC.root_directory);
|
||||
assert(p.has_root_directory() != TC.root_directory.empty());
|
||||
ASSERT_EQ(p.root_directory(), TC.root_directory);
|
||||
ASSERT_NEQ(p.has_root_directory(), TC.root_directory.empty());
|
||||
|
||||
assert(p.relative_path() == TC.relative_path);
|
||||
assert(p.has_relative_path() != TC.relative_path.empty());
|
||||
ASSERT_EQ(p.relative_path(), TC.relative_path);
|
||||
ASSERT_NEQ(p.has_relative_path(), TC.relative_path.empty());
|
||||
|
||||
assert(p.parent_path() == TC.parent_path);
|
||||
assert(p.has_parent_path() != TC.parent_path.empty());
|
||||
ASSERT_EQ(p.parent_path(), TC.parent_path);
|
||||
ASSERT_NEQ(p.has_parent_path(), TC.parent_path.empty());
|
||||
|
||||
assert(p.filename() == TC.filename);
|
||||
assert(p.has_filename() != TC.filename.empty());
|
||||
ASSERT_EQ(p.filename(), TC.filename);
|
||||
ASSERT_NEQ(p.has_filename(), TC.filename.empty());
|
||||
|
||||
assert(p.is_absolute() == p.has_root_directory());
|
||||
assert(p.is_relative() != p.is_absolute());
|
||||
ASSERT_EQ(p.is_absolute(), p.has_root_directory());
|
||||
ASSERT_NEQ(p.is_relative(), p.is_absolute());
|
||||
if (p.empty())
|
||||
ASSERT(p.is_relative());
|
||||
|
||||
assert(checkCollectionsEqual(p.begin(), p.end(),
|
||||
TC.elements.begin(), TC.elements.end()));
|
||||
ASSERT_COLLECTION_EQ_COMP(
|
||||
p.begin(), p.end(),
|
||||
TC.elements.begin(), TC.elements.end(),
|
||||
ComparePathExact()
|
||||
);
|
||||
// check backwards
|
||||
|
||||
std::vector<fs::path> Parts;
|
||||
for (auto it = p.end(); it != p.begin(); )
|
||||
Parts.push_back(*--it);
|
||||
assert(checkCollectionsEqual(Parts.begin(), Parts.end(),
|
||||
TC.elements.rbegin(), TC.elements.rend()));
|
||||
ASSERT_COLLECTION_EQ_COMP(Parts.begin(), Parts.end(),
|
||||
TC.elements.rbegin(), TC.elements.rend(),
|
||||
ComparePathExact());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,10 +180,12 @@ const FilenameDecompTestcase FilenameTestCases[] =
|
||||
{"", "", "", ""}
|
||||
, {".", ".", ".", ""}
|
||||
, {"..", "..", "..", ""}
|
||||
, {"/", "/", "/", ""}
|
||||
, {"/", "", "", ""}
|
||||
, {"foo", "foo", "foo", ""}
|
||||
, {"/foo/bar.txt", "bar.txt", "bar", ".txt"}
|
||||
, {"foo..txt", "foo..txt", "foo.", ".txt"}
|
||||
, {".profile", ".profile", ".profile", ""}
|
||||
, {".profile.txt", ".profile.txt", ".profile", ".txt"}
|
||||
};
|
||||
|
||||
|
||||
@@ -174,18 +193,19 @@ void decompFilenameTest()
|
||||
{
|
||||
using namespace fs;
|
||||
for (auto const & TC : FilenameTestCases) {
|
||||
path p(TC.raw);
|
||||
assert(p == TC.raw);
|
||||
CHECKPOINT(TC.raw.c_str());
|
||||
fs::path p(TC.raw);
|
||||
ASSERT_EQ(p, TC.raw);
|
||||
ASSERT_NOEXCEPT(p.empty());
|
||||
|
||||
assert(p.filename() == TC.filename);
|
||||
assert(p.has_filename() != TC.filename.empty());
|
||||
ASSERT_EQ(p.filename(), TC.filename);
|
||||
ASSERT_NEQ(p.has_filename(), TC.filename.empty());
|
||||
|
||||
assert(p.stem() == TC.stem);
|
||||
assert(p.has_stem() != TC.stem.empty());
|
||||
ASSERT_EQ(p.stem(), TC.stem);
|
||||
ASSERT_NEQ(p.has_stem(), TC.stem.empty());
|
||||
|
||||
assert(p.extension() == TC.extension);
|
||||
assert(p.has_extension() != TC.extension.empty());
|
||||
ASSERT_EQ(p.extension(), TC.extension);
|
||||
ASSERT_NEQ(p.has_extension(), TC.extension.empty());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03
|
||||
|
||||
// <experimental/filesystem>
|
||||
|
||||
// class path
|
||||
|
||||
// path lexically_normal() const;
|
||||
|
||||
#include "filesystem_include.hpp"
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "test_iterators.h"
|
||||
#include "count_new.hpp"
|
||||
#include "filesystem_test_helper.hpp"
|
||||
|
||||
|
||||
int main() {
|
||||
// clang-format off
|
||||
struct {
|
||||
std::string input;
|
||||
std::string expect;
|
||||
} TestCases[] = {
|
||||
{"", ""},
|
||||
{"/a/b/c", "/a/b/c"},
|
||||
{"/a/b//c", "/a/b/c"},
|
||||
{"foo/./bar/..", "foo/"},
|
||||
{"foo/.///bar/../", "foo/"},
|
||||
{"/a/b/", "/a/b/"},
|
||||
{"a/b", "a/b"},
|
||||
{"a/b/.", "a/b/"},
|
||||
{"a/b/./", "a/b/"},
|
||||
{"a/..", "."},
|
||||
{".", "."},
|
||||
{"./", "."},
|
||||
{"./.", "."},
|
||||
{"./..", ".."},
|
||||
{"..", ".."},
|
||||
{"../..", "../.."},
|
||||
{"/../", "/"},
|
||||
{"/../..", "/"},
|
||||
{"/../../", "/"},
|
||||
{"..", ".."},
|
||||
{"../", ".."},
|
||||
{"/a/b/c/../", "/a/b/"},
|
||||
{"/a/b/./", "/a/b/"},
|
||||
{"/a/b/c/../d", "/a/b/d"},
|
||||
{"/a/b/c/../d/", "/a/b/d/"},
|
||||
{"//a/", "/a/"},
|
||||
{"//a/b/", "/a/b/"},
|
||||
{"//a/b/.", "/a/b/"},
|
||||
{"//a/..", "/"},
|
||||
///===---------------------------------------------------------------===//
|
||||
/// Tests specifically for the clauses under [fs.path.generic]p6
|
||||
///===---------------------------------------------------------------===//
|
||||
// p1: If the path is empty, stop.
|
||||
{"", ""},
|
||||
// p2: Replace each slash character in the root-name with a preferred
|
||||
// separator.
|
||||
{"NO_ROOT_NAME_ON_LINUX", "NO_ROOT_NAME_ON_LINUX"},
|
||||
// p3: Replace each directory-separator with a preferred-separator.
|
||||
// [ Note: The generic pathname grammar ([fs.path.generic]) defines
|
||||
// directory-separator as one or more slashes and preferred-separators.
|
||||
// — end note ]
|
||||
{"/", "/"},
|
||||
{"//", "/"},
|
||||
{"///", "/"},
|
||||
{"a/b", "a/b"},
|
||||
{"a//b", "a/b"},
|
||||
{"a///b", "a/b"},
|
||||
{"a/b/", "a/b/"},
|
||||
{"a/b//", "a/b/"},
|
||||
{"a/b///", "a/b/"},
|
||||
{"///a////b//////", "/a/b/"},
|
||||
// p4: Remove each dot filename and any immediately following directory
|
||||
// separators
|
||||
{"foo/.", "foo/"},
|
||||
{"foo/./bar/.", "foo/bar/"},
|
||||
{"./foo/././bar/./", "foo/bar/"},
|
||||
{".///foo//.////./bar/.///", "foo/bar/"},
|
||||
// p5: As long as any appear, remove a non-dot-dot filename immediately
|
||||
// followed by a directory-separator and a dot-dot filename, along with
|
||||
// any immediately following directory separator.
|
||||
{"foo/..", "."},
|
||||
{"foo/../", "."},
|
||||
{"foo/bar/..", "foo/"},
|
||||
{"foo/bar/../", "foo/"},
|
||||
{"foo/bar/../..", "."},
|
||||
{"foo/bar/../../", "."},
|
||||
{"foo/bar/baz/../..", "foo/"},
|
||||
{"foo/bar/baz/../../", "foo/"},
|
||||
{"foo/bar/./..", "foo/"},
|
||||
{"foo/bar/./../", "foo/"},
|
||||
// p6: If there is a root-directory, remove all dot-dot filenames and any
|
||||
// directory-separators immediately following them. [ Note: These dot-dot
|
||||
// filenames attempt to refer to nonexistent parent directories. — end note ]
|
||||
{"/..", "/"},
|
||||
{"/../", "/"},
|
||||
{"/foo/../..", "/"},
|
||||
{"/../foo", "/foo"},
|
||||
{"/../foo/../..", "/"},
|
||||
// p7: If the last filename is dot-dot, remove any trailing
|
||||
// directory-separator.
|
||||
{"../", ".."},
|
||||
{"../../", "../.."},
|
||||
{"foo/../bar/../..///", ".."},
|
||||
{"foo/../bar/..//..///../", "../.."},
|
||||
// p8: If the path is empty, add a dot
|
||||
{".", "."},
|
||||
{"./", "."},
|
||||
{"foo/..", "."}
|
||||
};
|
||||
// clang-format on
|
||||
int ID = 0;
|
||||
bool Failed = false;
|
||||
for (auto& TC : TestCases) {
|
||||
++ID;
|
||||
fs::path p(TC.input);
|
||||
const fs::path output = p.lexically_normal();
|
||||
if (!PathEq(output, TC.expect)) {
|
||||
Failed = true;
|
||||
std::cerr << "TEST CASE #" << ID << " FAILED: \n";
|
||||
std::cerr << " Input: '" << TC.input << "'\n";
|
||||
std::cerr << " Expected: '" << TC.expect << "'\n";
|
||||
std::cerr << " Output: '" << output.native() << "'";
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
}
|
||||
return Failed;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03
|
||||
|
||||
// <experimental/filesystem>
|
||||
|
||||
// class path
|
||||
|
||||
// path lexically_relative(const path& p) const;
|
||||
// path lexically_proximate(const path& p) const;
|
||||
|
||||
#include "filesystem_include.hpp"
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "test_iterators.h"
|
||||
#include "count_new.hpp"
|
||||
#include "filesystem_test_helper.hpp"
|
||||
|
||||
|
||||
int main() {
|
||||
// clang-format off
|
||||
struct {
|
||||
std::string input;
|
||||
std::string base;
|
||||
std::string expect;
|
||||
} TestCases[] = {
|
||||
{"", "", "."},
|
||||
{"/", "a", ""},
|
||||
{"a", "/", ""},
|
||||
{"//net", "a", ""},
|
||||
{"a", "//net", ""},
|
||||
{"//net/", "//net", ""},
|
||||
{"//net", "//net/", ".."},
|
||||
{"//base", "a", ""},
|
||||
{"a", "a", "."},
|
||||
{"a/b", "a/b", "."},
|
||||
{"a/b/c/", "a/b/c/", "."},
|
||||
{"//net", "//net", "."},
|
||||
{"//net/", "//net/", "."},
|
||||
{"//net/a/b", "//net/a/b", "."},
|
||||
{"/a/d", "/a/b/c", "../../d"},
|
||||
{"/a/b/c", "/a/d", "../b/c"},
|
||||
{"a/b/c", "a", "b/c"},
|
||||
{"a/b/c", "a/b/c/x/y", "../.."},
|
||||
{"a/b/c", "a/b/c", "."},
|
||||
{"a/b", "c/d", "../../a/b"}
|
||||
};
|
||||
// clang-format on
|
||||
int ID = 0;
|
||||
bool Failed = false;
|
||||
for (auto& TC : TestCases) {
|
||||
++ID;
|
||||
const fs::path p(TC.input);
|
||||
const fs::path output = p.lexically_relative(TC.base);
|
||||
auto ReportErr = [&](const char* Testing, fs::path const& Output,
|
||||
fs::path const& Expected) {
|
||||
Failed = true;
|
||||
std::cerr << "TEST CASE #" << ID << " FAILED: \n";
|
||||
std::cerr << " Testing: " << Testing << "\n";
|
||||
std::cerr << " Input: '" << TC.input << "'\n";
|
||||
std::cerr << " Base: '" << TC.base << "'\n";
|
||||
std::cerr << " Expected: '" << Expected << "'\n";
|
||||
std::cerr << " Output: '" << Output.native() << "'";
|
||||
std::cerr << std::endl;
|
||||
};
|
||||
if (!PathEq(output, TC.expect))
|
||||
ReportErr("path::lexically_relative", output, TC.expect);
|
||||
const fs::path proximate_output = p.lexically_proximate(TC.base);
|
||||
// [path.gen] lexically_proximate
|
||||
// Returns: If the value of lexically_relative(base) is not an empty path,
|
||||
// return it.Otherwise return *this.
|
||||
const fs::path proximate_expected = output.native().empty() ? p
|
||||
: output;
|
||||
if (!PathEq(proximate_expected, proximate_output))
|
||||
ReportErr("path::lexically_proximate", proximate_output, proximate_expected);
|
||||
}
|
||||
return Failed;
|
||||
}
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "min_allocator.h"
|
||||
#include "filesystem_test_helper.hpp"
|
||||
|
||||
|
||||
MultiStringType longString = MKSTR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/123456789/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
||||
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "min_allocator.h"
|
||||
#include "filesystem_test_helper.hpp"
|
||||
|
||||
|
||||
MultiStringType longString = MKSTR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/123456789/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
||||
|
||||
int main()
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "test_iterators.h"
|
||||
#include "count_new.hpp"
|
||||
#include "filesystem_test_helper.hpp"
|
||||
|
||||
#include "verbose_assert.h"
|
||||
|
||||
struct RemoveFilenameTestcase {
|
||||
const char* value;
|
||||
@@ -33,27 +33,29 @@ struct RemoveFilenameTestcase {
|
||||
const RemoveFilenameTestcase TestCases[] =
|
||||
{
|
||||
{"", ""}
|
||||
, {"/", ""}
|
||||
, {"//", ""}
|
||||
, {"///", ""}
|
||||
, {"/", "/"}
|
||||
, {"//", "//"}
|
||||
, {"///", "///"}
|
||||
, {"\\", ""}
|
||||
, {".", ""}
|
||||
, {"..", ""}
|
||||
, {"/foo", "/"}
|
||||
, {"//foo", ""}
|
||||
, {"//foo/", ""}
|
||||
, {"//foo///", ""}
|
||||
, {"///foo", "/"}
|
||||
, {"///foo/", "///foo"}
|
||||
, {"/foo/", "/foo"}
|
||||
, {"/foo/.", "/foo"}
|
||||
, {"/foo/..", "/foo"}
|
||||
, {"/foo/////", "/foo"}
|
||||
, {"foo/bar", "foo/"}
|
||||
, {"foo/", "foo/"}
|
||||
, {"//foo", "//"}
|
||||
, {"//foo/", "//foo/"}
|
||||
, {"//foo///", "//foo///"}
|
||||
, {"///foo", "///"}
|
||||
, {"///foo/", "///foo/"}
|
||||
, {"/foo/", "/foo/"}
|
||||
, {"/foo/.", "/foo/"}
|
||||
, {"/foo/..", "/foo/"}
|
||||
, {"/foo/////", "/foo/////"}
|
||||
, {"/foo\\\\", "/"}
|
||||
, {"/foo//\\/", "/foo//\\"}
|
||||
, {"///foo", "/"}
|
||||
, {"/foo//\\/", "/foo//\\/"}
|
||||
, {"///foo", "///"}
|
||||
, {"file.txt", ""}
|
||||
, {"bar/../baz/./file.txt", "bar/../baz/."}
|
||||
, {"bar/../baz/./file.txt", "bar/../baz/./"}
|
||||
};
|
||||
|
||||
int main()
|
||||
@@ -64,16 +66,8 @@ int main()
|
||||
path p(p_orig);
|
||||
assert(p == TC.value);
|
||||
path& Ref = (p.remove_filename());
|
||||
assert(p == TC.expect);
|
||||
ASSERT_EQ(p, TC.expect) << DISPLAY(p_orig);
|
||||
assert(&Ref == &p);
|
||||
{
|
||||
const path parentp = p_orig.parent_path();
|
||||
if (parentp == p_orig.root_name()) {
|
||||
|
||||
assert(p.empty());
|
||||
} else {
|
||||
assert(p == parentp);
|
||||
}
|
||||
}
|
||||
assert(!p.has_filename());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
#include "test_iterators.h"
|
||||
#include "count_new.hpp"
|
||||
#include "filesystem_test_helper.hpp"
|
||||
|
||||
#include "assert_checkpoint.h"
|
||||
#include "verbose_assert.h"
|
||||
|
||||
struct ReplaceFilenameTestcase {
|
||||
const char* value;
|
||||
@@ -36,9 +37,9 @@ const ReplaceFilenameTestcase TestCases[] =
|
||||
{"/foo", "/bar", "bar"}
|
||||
, {"/foo", "/", ""}
|
||||
, {"foo", "bar", "bar"}
|
||||
, {"/", "bar", "bar"}
|
||||
, {"/", "/bar", "bar"}
|
||||
, {"\\", "bar", "bar"}
|
||||
, {"///", "bar", "bar"}
|
||||
, {"///", "///bar", "bar"}
|
||||
, {"\\\\", "bar", "bar"}
|
||||
, {"\\/\\", "\\/bar", "bar"}
|
||||
, {".", "bar", "bar"}
|
||||
@@ -52,9 +53,11 @@ int main()
|
||||
using namespace fs;
|
||||
for (auto const & TC : TestCases) {
|
||||
path p(TC.value);
|
||||
assert(p == TC.value);
|
||||
ASSERT_EQ(p, TC.value);
|
||||
path& Ref = (p.replace_filename(TC.filename));
|
||||
assert(p == TC.expect);
|
||||
ASSERT_EQ(p, TC.expect)
|
||||
<< DISPLAY(TC.value)
|
||||
<< DISPLAY(TC.filename);
|
||||
assert(&Ref == &p);
|
||||
// Tests Effects "as-if": remove_filename() append(filename)
|
||||
{
|
||||
@@ -62,7 +65,7 @@ int main()
|
||||
path replace(TC.filename);
|
||||
p2.remove_filename();
|
||||
p2 /= replace;
|
||||
assert(p2 == p);
|
||||
ASSERT_EQ(p, p2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03
|
||||
|
||||
// <experimental/filesystem>
|
||||
|
||||
// enum class perm_options;
|
||||
|
||||
#include "filesystem_include.hpp"
|
||||
#include <type_traits>
|
||||
#include <cassert>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "check_bitmask_types.hpp"
|
||||
|
||||
|
||||
constexpr fs::perm_options ME(int val) {
|
||||
return static_cast<fs::perm_options>(val);
|
||||
}
|
||||
|
||||
int main() {
|
||||
typedef fs::perm_options E;
|
||||
static_assert(std::is_enum<E>::value, "");
|
||||
|
||||
// Check that E is a scoped enum by checking for conversions.
|
||||
typedef std::underlying_type<E>::type UT;
|
||||
static_assert(!std::is_convertible<E, UT>::value, "");
|
||||
|
||||
static_assert(std::is_same<UT, unsigned char >::value, ""); // Implementation detail
|
||||
|
||||
typedef check_bitmask_type<E, E::replace, E::nofollow> BitmaskTester;
|
||||
assert(BitmaskTester::check());
|
||||
|
||||
static_assert(
|
||||
E::replace == ME(1) &&
|
||||
E::add == ME(2) &&
|
||||
E::remove == ME(4) &&
|
||||
E::nofollow == ME(8),
|
||||
"Expected enumeration values do not match");
|
||||
}
|
||||
@@ -28,89 +28,30 @@ TEST_SUITE(filesystem_absolute_path_test_suite)
|
||||
TEST_CASE(absolute_signature_test)
|
||||
{
|
||||
const path p; ((void)p);
|
||||
std::error_code ec;
|
||||
ASSERT_NOT_NOEXCEPT(absolute(p));
|
||||
ASSERT_NOT_NOEXCEPT(absolute(p, p));
|
||||
ASSERT_NOT_NOEXCEPT(absolute(p, ec));
|
||||
}
|
||||
|
||||
// There are 4 cases is the proposal for absolute path.
|
||||
// Each scope tests one of the cases.
|
||||
TEST_CASE(absolute_path_test)
|
||||
|
||||
TEST_CASE(basic_test)
|
||||
{
|
||||
// has_root_name() && has_root_directory()
|
||||
{
|
||||
const path p("//net/foo");
|
||||
const path base("//net/bar/baz");
|
||||
TEST_REQUIRE(p.has_root_name());
|
||||
TEST_REQUIRE(p.has_root_directory());
|
||||
TEST_CHECK(p.is_absolute());
|
||||
path ret = absolute(p, base);
|
||||
TEST_CHECK(ret.is_absolute());
|
||||
TEST_CHECK(ret == p);
|
||||
}
|
||||
// !has_root_name() && has_root_directory()
|
||||
{
|
||||
const path p("/foo");
|
||||
const path base("//net/bar");
|
||||
TEST_REQUIRE(not p.has_root_name());
|
||||
TEST_REQUIRE(p.has_root_directory());
|
||||
TEST_CHECK(p.is_absolute());
|
||||
// ensure absolute(base) is not recursively called
|
||||
TEST_REQUIRE(base.has_root_name());
|
||||
TEST_REQUIRE(base.has_root_directory());
|
||||
|
||||
path ret = absolute(p, base);
|
||||
TEST_CHECK(ret.is_absolute());
|
||||
TEST_CHECK(ret.has_root_name());
|
||||
TEST_CHECK(ret.root_name() == path("//net"));
|
||||
TEST_CHECK(ret.has_root_directory());
|
||||
TEST_CHECK(ret.root_directory() == path("/"));
|
||||
TEST_CHECK(ret == path("//net/foo"));
|
||||
}
|
||||
// has_root_name() && !has_root_directory()
|
||||
{
|
||||
const path p("//net");
|
||||
const path base("//net/foo/bar");
|
||||
TEST_REQUIRE(p.has_root_name());
|
||||
TEST_REQUIRE(not p.has_root_directory());
|
||||
TEST_CHECK(not p.is_absolute());
|
||||
// absolute is called recursively on base. The following conditions
|
||||
// must be true for it to return base unmodified
|
||||
TEST_REQUIRE(base.has_root_name());
|
||||
TEST_REQUIRE(base.has_root_directory());
|
||||
path ret = absolute(p, base);
|
||||
const path expect("//net/foo/bar");
|
||||
TEST_CHECK(ret.is_absolute());
|
||||
TEST_CHECK(ret == path("//net/foo/bar"));
|
||||
}
|
||||
// !has_root_name() && !has_root_directory()
|
||||
{
|
||||
const path p("bar/baz");
|
||||
const path base("//net/foo");
|
||||
TEST_REQUIRE(not p.has_root_name());
|
||||
TEST_REQUIRE(not p.has_root_directory());
|
||||
TEST_REQUIRE(base.has_root_name());
|
||||
TEST_REQUIRE(base.has_root_directory());
|
||||
|
||||
path ret = absolute(p, base);
|
||||
TEST_CHECK(ret.is_absolute());
|
||||
TEST_CHECK(ret == path("//net/foo/bar/baz"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(absolute_path_with_default_base)
|
||||
{
|
||||
const path testCases[] = {
|
||||
"//net/foo", // has_root_name() && has_root_directory()
|
||||
"/foo", // !has_root_name() && has_root_directory()
|
||||
"//net", // has_root_name() && !has_root_directory()
|
||||
"bar/baz" // !has_root_name() && !has_root_directory()
|
||||
const fs::path cwd = fs::current_path();
|
||||
const struct {
|
||||
std::string input;
|
||||
std::string expect;
|
||||
} TestCases [] = {
|
||||
{"", cwd / ""},
|
||||
{"foo", cwd / "foo"},
|
||||
{"foo/", cwd / "foo/"},
|
||||
{"/already_absolute", "/already_absolute"}
|
||||
};
|
||||
const path base = current_path();
|
||||
for (auto& p : testCases) {
|
||||
const path ret = absolute(p);
|
||||
const path expect = absolute(p, base);
|
||||
for (auto& TC : TestCases) {
|
||||
std::error_code ec = GetTestEC();
|
||||
const path ret = absolute(TC.input, ec);
|
||||
TEST_CHECK(!ec);
|
||||
TEST_CHECK(ret.is_absolute());
|
||||
TEST_CHECK(ret == expect);
|
||||
TEST_CHECK(PathEq(ret, TC.expect));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,8 @@
|
||||
|
||||
// <experimental/filesystem>
|
||||
|
||||
// path canonical(const path& p, const path& base = current_path());
|
||||
// path canonical(const path& p);
|
||||
// path canonical(const path& p, error_code& ec);
|
||||
// path canonical(const path& p, const path& base, error_code& ec);
|
||||
|
||||
#include "filesystem_include.hpp"
|
||||
#include <type_traits>
|
||||
@@ -25,6 +24,15 @@
|
||||
|
||||
using namespace fs;
|
||||
|
||||
struct CWDGuard {
|
||||
path OldCWD;
|
||||
CWDGuard() : OldCWD(fs::current_path()) { }
|
||||
~CWDGuard() { fs::current_path(OldCWD); }
|
||||
|
||||
CWDGuard(CWDGuard const&) = delete;
|
||||
CWDGuard& operator=(CWDGuard const&) = delete;
|
||||
};
|
||||
|
||||
TEST_SUITE(filesystem_canonical_path_test_suite)
|
||||
|
||||
TEST_CASE(signature_test)
|
||||
@@ -32,15 +40,14 @@ TEST_CASE(signature_test)
|
||||
const path p; ((void)p);
|
||||
std::error_code ec; ((void)ec);
|
||||
ASSERT_NOT_NOEXCEPT(canonical(p));
|
||||
ASSERT_NOT_NOEXCEPT(canonical(p, p));
|
||||
ASSERT_NOT_NOEXCEPT(canonical(p, ec));
|
||||
ASSERT_NOT_NOEXCEPT(canonical(p, p, ec));
|
||||
}
|
||||
|
||||
// There are 4 cases is the proposal for absolute path.
|
||||
// Each scope tests one of the cases.
|
||||
TEST_CASE(test_canonical)
|
||||
{
|
||||
CWDGuard guard;
|
||||
// has_root_name() && has_root_directory()
|
||||
const path Root = StaticEnv::Root;
|
||||
const path RootName = Root.filename();
|
||||
@@ -65,54 +72,51 @@ TEST_CASE(test_canonical)
|
||||
{ SymlinkName, StaticEnv::File, StaticEnv::Root}
|
||||
};
|
||||
for (auto& TC : testCases) {
|
||||
std::error_code ec;
|
||||
const path ret = canonical(TC.p, TC.base, ec);
|
||||
std::error_code ec = GetTestEC();
|
||||
fs::current_path(TC.base);
|
||||
const path ret = canonical(TC.p, ec);
|
||||
TEST_REQUIRE(!ec);
|
||||
const path ret2 = canonical(TC.p, TC.base);
|
||||
TEST_CHECK(ret == TC.expect);
|
||||
TEST_CHECK(ret == ret2);
|
||||
const path ret2 = canonical(TC.p);
|
||||
TEST_CHECK(PathEq(ret, TC.expect));
|
||||
TEST_CHECK(PathEq(ret, ret2));
|
||||
TEST_CHECK(ret.is_absolute());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(test_dne_path)
|
||||
{
|
||||
std::error_code ec;
|
||||
std::error_code ec = GetTestEC();
|
||||
{
|
||||
const path ret = canonical(StaticEnv::DNE, ec);
|
||||
TEST_REQUIRE(ec);
|
||||
TEST_CHECK(ret == path{});
|
||||
}
|
||||
ec.clear();
|
||||
{
|
||||
const path ret = canonical(StaticEnv::DNE, StaticEnv::Root, ec);
|
||||
TEST_CHECK(ec != GetTestEC());
|
||||
TEST_REQUIRE(ec);
|
||||
TEST_CHECK(ret == path{});
|
||||
}
|
||||
{
|
||||
TEST_CHECK_THROW(filesystem_error, canonical(StaticEnv::DNE));
|
||||
TEST_CHECK_THROW(filesystem_error, canonical(StaticEnv::DNE, StaticEnv::Root));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(test_exception_contains_paths)
|
||||
{
|
||||
#ifndef TEST_HAS_NO_EXCEPTIONS
|
||||
CWDGuard guard;
|
||||
const path p = "blabla/dne";
|
||||
const path base = StaticEnv::Root;
|
||||
try {
|
||||
canonical(p, base);
|
||||
TEST_REQUIRE(false);
|
||||
} catch (filesystem_error const& err) {
|
||||
TEST_CHECK(err.path1() == p);
|
||||
TEST_CHECK(err.path2() == base);
|
||||
}
|
||||
try {
|
||||
canonical(p);
|
||||
TEST_REQUIRE(false);
|
||||
} catch (filesystem_error const& err) {
|
||||
TEST_CHECK(err.path1() == p);
|
||||
TEST_CHECK(err.path2() == current_path());
|
||||
// libc++ provides the current path as the second path in the exception
|
||||
LIBCPP_ONLY(TEST_CHECK(err.path2() == current_path()));
|
||||
}
|
||||
fs::current_path(StaticEnv::Dir);
|
||||
try {
|
||||
canonical(p);
|
||||
TEST_REQUIRE(false);
|
||||
} catch (filesystem_error const& err) {
|
||||
TEST_CHECK(err.path1() == p);
|
||||
LIBCPP_ONLY(TEST_CHECK(err.path2() == StaticEnv::Dir));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
// void last_write_time(const path& p, file_time_type new_type,
|
||||
// std::error_code& ec) noexcept;
|
||||
|
||||
|
||||
#include "filesystem_include.hpp"
|
||||
#include <type_traits>
|
||||
#include <chrono>
|
||||
@@ -33,7 +32,6 @@
|
||||
|
||||
using namespace fs;
|
||||
|
||||
|
||||
std::pair<std::time_t, std::time_t> GetTimes(path const& p) {
|
||||
using Clock = file_time_type::clock;
|
||||
struct ::stat st;
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03
|
||||
|
||||
// <experimental/filesystem>
|
||||
|
||||
// path proximate(const path& p, error_code &ec)
|
||||
// path proximate(const path& p, const path& base = current_path())
|
||||
// path proximate(const path& p, const path& base, error_code& ec);
|
||||
|
||||
#include "filesystem_include.hpp"
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "test_iterators.h"
|
||||
#include "count_new.hpp"
|
||||
#include "rapid-cxx-test.hpp"
|
||||
#include "filesystem_test_helper.hpp"
|
||||
|
||||
|
||||
static int count_path_elems(const fs::path& p) {
|
||||
int count = 0;
|
||||
for (auto& elem : p) {
|
||||
if (elem != "/" && elem != "")
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
TEST_SUITE(filesystem_proximate_path_test_suite)
|
||||
|
||||
|
||||
TEST_CASE(signature_test)
|
||||
{
|
||||
using fs::path;
|
||||
const path p; ((void)p);
|
||||
std::error_code ec; ((void)ec);
|
||||
ASSERT_NOT_NOEXCEPT(proximate(p));
|
||||
ASSERT_NOT_NOEXCEPT(proximate(p, p));
|
||||
ASSERT_NOT_NOEXCEPT(proximate(p, ec));
|
||||
ASSERT_NOT_NOEXCEPT(proximate(p, p, ec));
|
||||
}
|
||||
|
||||
TEST_CASE(basic_test) {
|
||||
using fs::path;
|
||||
const path cwd = fs::current_path();
|
||||
const path parent_cwd = cwd.parent_path();
|
||||
const path curdir = cwd.filename();
|
||||
TEST_REQUIRE(!cwd.native().empty());
|
||||
int cwd_depth = count_path_elems(cwd);
|
||||
path dot_dot_to_root;
|
||||
for (int i=0; i < cwd_depth; ++i)
|
||||
dot_dot_to_root /= "..";
|
||||
path relative_cwd = cwd.native().substr(1);
|
||||
// clang-format off
|
||||
struct {
|
||||
std::string input;
|
||||
std::string base;
|
||||
std::string expect;
|
||||
} TestCases[] = {
|
||||
{"", "", "."},
|
||||
{cwd, "a", ".."},
|
||||
{parent_cwd, "a", "../.."},
|
||||
{"a", cwd, "a"},
|
||||
{"a", parent_cwd, "fs.op.proximate/a"},
|
||||
{"/", "a", dot_dot_to_root / ".."},
|
||||
{"/", "a/b", dot_dot_to_root / "../.."},
|
||||
{"/", "a/b/", dot_dot_to_root / "../../.."},
|
||||
{"a", "/", relative_cwd / "a"},
|
||||
{"a/b", "/", relative_cwd / "a/b"},
|
||||
{"a", "/net", ".." / relative_cwd / "a"},
|
||||
{"//net/", "//net", "/net/"},
|
||||
{"//net", "//net/", ".."},
|
||||
{"//net", "//net", "."},
|
||||
{"//net/", "//net/", "."},
|
||||
{"//base", "a", dot_dot_to_root / "../base"},
|
||||
{"a", "a", "."},
|
||||
{"a/b", "a/b", "."},
|
||||
{"a/b/c/", "a/b/c/", "."},
|
||||
{"//net/a/b", "//net/a/b", "."},
|
||||
{"/a/d", "/a/b/c", "../../d"},
|
||||
{"/a/b/c", "/a/d", "../b/c"},
|
||||
{"a/b/c", "a", "b/c"},
|
||||
{"a/b/c", "a/b/c/x/y", "../.."},
|
||||
{"a/b/c", "a/b/c", "."},
|
||||
{"a/b", "c/d", "../../a/b"}
|
||||
};
|
||||
// clang-format on
|
||||
int ID = 0;
|
||||
for (auto& TC : TestCases) {
|
||||
++ID;
|
||||
std::error_code ec = GetTestEC();
|
||||
fs::path p(TC.input);
|
||||
const fs::path output = fs::proximate(p, TC.base, ec);
|
||||
TEST_CHECK(!ec);
|
||||
TEST_CHECK(PathEq(output, TC.expect));
|
||||
if (!PathEq(output, TC.expect)) {
|
||||
const path canon_input = fs::weakly_canonical(TC.input);
|
||||
const path canon_base = fs::weakly_canonical(TC.base);
|
||||
const path lexically_p = canon_input.lexically_proximate(canon_base);
|
||||
std::cerr << "TEST CASE #" << ID << " FAILED: \n";
|
||||
std::cerr << " Input: '" << TC.input << "'\n";
|
||||
std::cerr << " Base: '" << TC.base << "'\n";
|
||||
std::cerr << " Expected: '" << TC.expect << "'\n";
|
||||
std::cerr << " Output: '" << output.native() << "'\n";
|
||||
std::cerr << " Lex Prox: '" << lexically_p.native() << "'\n";
|
||||
std::cerr << " Canon Input: " << canon_input << "\n";
|
||||
std::cerr << " Canon Base: " << canon_base << "\n";
|
||||
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END()
|
||||
@@ -0,0 +1,78 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03
|
||||
|
||||
// <experimental/filesystem>
|
||||
|
||||
// path proximate(const path& p, error_code &ec)
|
||||
// path proximate(const path& p, const path& base = current_path())
|
||||
// path proximate(const path& p, const path& base, error_code& ec);
|
||||
|
||||
#include "filesystem_include.hpp"
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "test_iterators.h"
|
||||
#include "count_new.hpp"
|
||||
#include "rapid-cxx-test.hpp"
|
||||
#include "filesystem_test_helper.hpp"
|
||||
|
||||
|
||||
TEST_SUITE(filesystem_proximate_path_test_suite)
|
||||
|
||||
TEST_CASE(test_signature) {
|
||||
|
||||
}
|
||||
int main() {
|
||||
// clang-format off
|
||||
struct {
|
||||
std::string input;
|
||||
std::string expect;
|
||||
} TestCases[] = {
|
||||
{"", fs::current_path()},
|
||||
{".", fs::current_path()},
|
||||
{StaticEnv::File, StaticEnv::File},
|
||||
{StaticEnv::Dir, StaticEnv::Dir},
|
||||
{StaticEnv::SymlinkToDir, StaticEnv::Dir},
|
||||
{StaticEnv::SymlinkToDir / "dir2/.", StaticEnv::Dir / "dir2"},
|
||||
// FIXME? If the trailing separator occurs in a part of the path that exists,
|
||||
// it is ommitted. Otherwise it is added to the end of the result.
|
||||
{StaticEnv::SymlinkToDir / "dir2/./", StaticEnv::Dir / "dir2"},
|
||||
{StaticEnv::SymlinkToDir / "dir2/DNE/./", StaticEnv::Dir / "dir2/DNE/"},
|
||||
{StaticEnv::SymlinkToDir / "dir2", StaticEnv::Dir2},
|
||||
{StaticEnv::SymlinkToDir / "dir2/../dir2/DNE/..", StaticEnv::Dir2 / ""},
|
||||
{StaticEnv::SymlinkToDir / "dir2/dir3/../DNE/DNE2", StaticEnv::Dir2 / "DNE/DNE2"},
|
||||
{StaticEnv::Dir / "../dir1", StaticEnv::Dir},
|
||||
{StaticEnv::Dir / "./.", StaticEnv::Dir},
|
||||
{StaticEnv::Dir / "DNE/../foo", StaticEnv::Dir / "foo"}
|
||||
};
|
||||
// clang-format on
|
||||
int ID = 0;
|
||||
bool Failed = false;
|
||||
for (auto& TC : TestCases) {
|
||||
++ID;
|
||||
fs::path p(TC.input);
|
||||
const fs::path output = fs::weakly_canonical(p);
|
||||
if (output != TC.expect) {
|
||||
Failed = true;
|
||||
std::cerr << "TEST CASE #" << ID << " FAILED: \n";
|
||||
std::cerr << " Input: '" << TC.input << "'\n";
|
||||
std::cerr << " Expected: '" << TC.expect << "'\n";
|
||||
std::cerr << " Output: '" << output.native() << "'";
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
}
|
||||
return Failed;
|
||||
}
|
||||
|
||||
TEST_SUITE_END()
|
||||
@@ -1,58 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03
|
||||
|
||||
// <experimental/filesystem>
|
||||
|
||||
// path system_complete(const path& p);
|
||||
// path system_complete(const path& p, error_code& ec);
|
||||
|
||||
// Note: For POSIX based operating systems, 'system_complete(p)' has the
|
||||
// same semantics as 'absolute(p, current_path())'.
|
||||
|
||||
#include "filesystem_include.hpp"
|
||||
#include <type_traits>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "rapid-cxx-test.hpp"
|
||||
#include "filesystem_test_helper.hpp"
|
||||
|
||||
using namespace fs;
|
||||
|
||||
TEST_SUITE(filesystem_system_complete_test_suite)
|
||||
|
||||
TEST_CASE(signature_test)
|
||||
{
|
||||
const path p; ((void)p);
|
||||
std::error_code ec; ((void)ec);
|
||||
ASSERT_NOT_NOEXCEPT(system_complete(p));
|
||||
ASSERT_NOT_NOEXCEPT(system_complete(p, ec));
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE(basic_system_complete_tests)
|
||||
{
|
||||
const path testCases[] = {
|
||||
"//net/foo", // has_root_name() && has_root_directory()
|
||||
"/foo", // !has_root_name() && has_root_directory()
|
||||
"//net", // has_root_name() && !has_root_directory()
|
||||
"bar/baz" // !has_root_name() && !has_root_directory()
|
||||
};
|
||||
const path base = current_path();
|
||||
for (auto& p : testCases) {
|
||||
const path ret = system_complete(p);
|
||||
const path expect = absolute(p, base);
|
||||
TEST_CHECK(ret.is_absolute());
|
||||
TEST_CHECK(ret == expect);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END()
|
||||
@@ -0,0 +1,76 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03
|
||||
|
||||
// <experimental/filesystem>
|
||||
|
||||
// path weakly_canonical(const path& p);
|
||||
// path weakly_canonical(const path& p, error_code& ec);
|
||||
|
||||
#include "filesystem_include.hpp"
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "test_iterators.h"
|
||||
#include "count_new.hpp"
|
||||
#include "filesystem_test_helper.hpp"
|
||||
|
||||
|
||||
int main() {
|
||||
// clang-format off
|
||||
struct {
|
||||
std::string input;
|
||||
std::string expect;
|
||||
} TestCases[] = {
|
||||
{"", fs::current_path()},
|
||||
{".", fs::current_path()},
|
||||
{"/", "/"},
|
||||
{"/foo", "/foo"},
|
||||
{"/.", "/"},
|
||||
{"/./", "/"},
|
||||
{"a/b", fs::current_path() / "a/b"},
|
||||
{"a", fs::current_path() / "a"},
|
||||
{"a/b/", fs::current_path() / "a/b/"},
|
||||
{StaticEnv::File, StaticEnv::File},
|
||||
{StaticEnv::Dir, StaticEnv::Dir},
|
||||
{StaticEnv::SymlinkToDir, StaticEnv::Dir},
|
||||
{StaticEnv::SymlinkToDir / "dir2/.", StaticEnv::Dir / "dir2"},
|
||||
// FIXME? If the trailing separator occurs in a part of the path that exists,
|
||||
// it is ommitted. Otherwise it is added to the end of the result.
|
||||
{StaticEnv::SymlinkToDir / "dir2/./", StaticEnv::Dir / "dir2"},
|
||||
{StaticEnv::SymlinkToDir / "dir2/DNE/./", StaticEnv::Dir / "dir2/DNE/"},
|
||||
{StaticEnv::SymlinkToDir / "dir2", StaticEnv::Dir2},
|
||||
{StaticEnv::SymlinkToDir / "dir2/../dir2/DNE/..", StaticEnv::Dir2 / ""},
|
||||
{StaticEnv::SymlinkToDir / "dir2/dir3/../DNE/DNE2", StaticEnv::Dir2 / "DNE/DNE2"},
|
||||
{StaticEnv::Dir / "../dir1", StaticEnv::Dir},
|
||||
{StaticEnv::Dir / "./.", StaticEnv::Dir},
|
||||
{StaticEnv::Dir / "DNE/../foo", StaticEnv::Dir / "foo"}
|
||||
};
|
||||
// clang-format on
|
||||
int ID = 0;
|
||||
bool Failed = false;
|
||||
for (auto& TC : TestCases) {
|
||||
++ID;
|
||||
fs::path p(TC.input);
|
||||
const fs::path output = fs::weakly_canonical(p);
|
||||
if (!PathEq(output, TC.expect)) {
|
||||
Failed = true;
|
||||
std::cerr << "TEST CASE #" << ID << " FAILED: \n";
|
||||
std::cerr << " Input: '" << TC.input << "'\n";
|
||||
std::cerr << " Expected: '" << TC.expect << "'\n";
|
||||
std::cerr << " Output: '" << output.native() << "'";
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
}
|
||||
return Failed;
|
||||
}
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
|
||||
|
||||
// static test helpers
|
||||
|
||||
#ifndef LIBCXX_FILESYSTEM_STATIC_TEST_ROOT
|
||||
@@ -400,4 +399,8 @@ void SleepFor(std::chrono::seconds dur) {
|
||||
;
|
||||
}
|
||||
|
||||
inline bool PathEq(fs::path const& LHS, fs::path const& RHS) {
|
||||
return LHS.native() == RHS.native();
|
||||
}
|
||||
|
||||
#endif /* FILESYSTEM_TEST_HELPER_HPP */
|
||||
|
||||
222
test/support/verbose_assert.h
Normal file
222
test/support/verbose_assert.h
Normal file
@@ -0,0 +1,222 @@
|
||||
#ifndef TEST_SUPPORT_VERBOSE_ASSERT
|
||||
#define TEST_SUPPORT_VERBOSE_ASSERT
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "test_macros.h"
|
||||
|
||||
namespace verbose_assert {
|
||||
|
||||
typedef std::basic_ostream<char>&(EndLType)(std::basic_ostream<char>&);
|
||||
|
||||
template <class Stream, class Tp,
|
||||
class = decltype(std::declval<Stream&>() << std::declval<Tp const&>())>
|
||||
std::true_type IsStreamableImp(int);
|
||||
template <class Stream, class Tp> std::false_type IsStreamableImp(long);
|
||||
|
||||
template <class Stream, class Tp>
|
||||
struct IsStreamable : decltype(IsStreamableImp<Stream, Tp>(0)) {};
|
||||
|
||||
template <class Tp, int ST = (IsStreamable<decltype(std::cerr), Tp>::value ? 1
|
||||
: (IsStreamable<decltype(std::wcerr), Tp>::value ? 2 : -1))>
|
||||
struct SelectStream {
|
||||
static_assert(ST == -1, "specialization required for ST != -1");
|
||||
static void Print(Tp const&) { std::clog << "Value Not Streamable!\n"; }
|
||||
};
|
||||
|
||||
template <class Tp>
|
||||
struct SelectStream<Tp, 1> {
|
||||
static void Print(Tp const& val) { std::cerr << val; }
|
||||
};
|
||||
|
||||
template <class Tp>
|
||||
struct SelectStream<Tp, 2> {
|
||||
static void Print(Tp const& val) { std::wcerr << val; }
|
||||
};
|
||||
|
||||
struct AssertData {
|
||||
AssertData(const char* xcheck, const char* xfile, const char* xfunc,
|
||||
unsigned long xline, bool xpassed = true)
|
||||
: passed(xpassed), check(xcheck), file(xfile), func(xfunc), line(xline),
|
||||
msg() {}
|
||||
|
||||
AssertData& SetFailed(std::string xmsg = std::string()) {
|
||||
msg = xmsg;
|
||||
passed = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void PrintFailed() const {
|
||||
std::fprintf(stderr, "%s:%lu %s: Assertion '%s' failed.\n", file, line,
|
||||
func, check);
|
||||
if (!msg.empty())
|
||||
std::fprintf(stderr, "%s\n", msg.data());
|
||||
}
|
||||
|
||||
bool passed;
|
||||
const char* check;
|
||||
const char* file;
|
||||
const char* func;
|
||||
unsigned long line;
|
||||
std::string msg;
|
||||
};
|
||||
|
||||
// AssertHandler is the class constructed by failing CHECK macros. AssertHandler
|
||||
// will log information about the failures and abort when it is destructed.
|
||||
class AssertHandler {
|
||||
public:
|
||||
AssertHandler(AssertData const& Data)
|
||||
: passed(Data.passed) {
|
||||
if (!passed)
|
||||
Data.PrintFailed();
|
||||
}
|
||||
|
||||
~AssertHandler() TEST_NOEXCEPT_FALSE {
|
||||
if (!passed) {
|
||||
error_log << std::endl;
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
class LogType {
|
||||
friend class AssertHandler;
|
||||
|
||||
template <class Tp>
|
||||
friend LogType& operator<<(LogType& log, Tp const& value) {
|
||||
if (!log.is_disabled) {
|
||||
SelectStream<Tp>::Print(value);
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
friend LogType& operator<<(LogType& log, EndLType* m) {
|
||||
if (!log.is_disabled) {
|
||||
SelectStream<EndLType*>::Print(m);
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
private:
|
||||
LogType(bool disable) : is_disabled(disable) {}
|
||||
bool is_disabled;
|
||||
|
||||
LogType(LogType const&);
|
||||
LogType& operator=(LogType const&);
|
||||
};
|
||||
|
||||
LogType& GetLog() {
|
||||
if (passed)
|
||||
return null_log;
|
||||
return error_log;
|
||||
}
|
||||
|
||||
private:
|
||||
static LogType null_log;
|
||||
static LogType error_log;
|
||||
|
||||
AssertHandler& operator=(const AssertHandler&) = delete;
|
||||
AssertHandler(const AssertHandler&) = delete;
|
||||
AssertHandler() = delete;
|
||||
|
||||
private:
|
||||
bool passed;
|
||||
};
|
||||
|
||||
AssertHandler::LogType AssertHandler::null_log(true);
|
||||
AssertHandler::LogType AssertHandler::error_log(false);
|
||||
|
||||
template <class It1>
|
||||
std::string PrintRange(const char* Name, It1 F, It1 E) {
|
||||
std::stringstream ss;
|
||||
ss << " " << Name << " = [";
|
||||
while (F != E) {
|
||||
ss << *F;
|
||||
++F;
|
||||
if (F != E)
|
||||
ss << ", ";
|
||||
}
|
||||
ss << "]\n";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template <class Tp, class Up>
|
||||
std::string PrintMismatch(Tp const& LHS, Up const& RHS, int Elem) {
|
||||
std::stringstream ss;
|
||||
ss << " Element " << Elem << " mismatched: `" << LHS << "` != `" << RHS
|
||||
<< "`!\n";
|
||||
return ss.str();
|
||||
};
|
||||
|
||||
struct EqualToComp {
|
||||
template <class Tp, class Up>
|
||||
bool operator()(Tp const& LHS, Up const& RHS) const {
|
||||
return LHS == RHS;
|
||||
}
|
||||
};
|
||||
|
||||
template <class It1, class It2, class Comp>
|
||||
AssertData CheckCollectionsEqual(It1 F1, It1 E1, It2 F2, It2 E2,
|
||||
AssertData Data, Comp C = EqualToComp()) {
|
||||
const It1 F1Orig = F1;
|
||||
const It2 F2Orig = F2;
|
||||
bool Failed = false;
|
||||
std::string ErrorMsg;
|
||||
int Idx = 0;
|
||||
while (F1 != E1 && F2 != E2) {
|
||||
if (!(C(*F1, *F2))) {
|
||||
ErrorMsg += PrintMismatch(*F1, *F2, Idx);
|
||||
Failed = true;
|
||||
break;
|
||||
}
|
||||
++Idx;
|
||||
++F1;
|
||||
++F2;
|
||||
}
|
||||
if (!Failed && (F1 != E1 || F2 != E2)) {
|
||||
ErrorMsg += " Ranges have different sizes!\n";
|
||||
Failed = true;
|
||||
}
|
||||
if (Failed) {
|
||||
ErrorMsg += PrintRange("LHS", F1Orig, E1);
|
||||
ErrorMsg += PrintRange("RHS", F2Orig, E2);
|
||||
Data.SetFailed(ErrorMsg);
|
||||
}
|
||||
return Data;
|
||||
}
|
||||
} // namespace verbose_assert
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define ASSERT_FN_NAME() __PRETTY_FUNCTION__
|
||||
#else
|
||||
#define ASSERT_FN_NAME() __func__
|
||||
#endif
|
||||
|
||||
#define DISPLAY(...) " " #__VA_ARGS__ " = " << (__VA_ARGS__) << "\n"
|
||||
|
||||
#define ASSERT(...) \
|
||||
::verbose_assert::AssertHandler(::verbose_assert::AssertData( \
|
||||
#__VA_ARGS__, __FILE__, ASSERT_FN_NAME(), __LINE__,(__VA_ARGS__))).GetLog()
|
||||
|
||||
#define ASSERT_EQ(LHS, RHS) \
|
||||
ASSERT(LHS == RHS) << DISPLAY(LHS) << DISPLAY(RHS)
|
||||
#define ASSERT_NEQ(LHS, RHS) \
|
||||
ASSERT(LHS != RHS) << DISPLAY(LHS) << DISPLAY(RHS)
|
||||
#define ASSERT_PRED(PRED, LHS, RHS) \
|
||||
ASSERT(PRED(LHS, RHS)) << DISPLAY(LHS) << DISPLAY(RHS)
|
||||
|
||||
#define ASSERT_COLLECTION_EQ_COMP(F1, E1, F2, E2, Comp) \
|
||||
(::verbose_assert::AssertHandler( \
|
||||
::verbose_assert::CheckCollectionsEqual( \
|
||||
F1, E1, F2, E2, \
|
||||
::verbose_assert::AssertData("CheckCollectionsEqual(" #F1 ", " #E1 \
|
||||
", " #F2 ", " #E2 ")", \
|
||||
__FILE__, ASSERT_FN_NAME(), __LINE__), \
|
||||
Comp)) \
|
||||
.GetLog())
|
||||
|
||||
#define ASSERT_COLLECTION_EQ(F1, E1, F2, E2) \
|
||||
ASSERT_COLLECTION_EQ_COMP(F1, E1, F2, E2, ::verbose_assert::EqualToComp())
|
||||
|
||||
#endif
|
||||
@@ -111,7 +111,7 @@
|
||||
<tr><td><a href="https://wg21.link/p0180r2">p0180r2</a></td><td>LWG</td><td>Reserve a New Library Namespace for Future Standardization</td><td>Oulu</td><td><i>Nothing to do</i></td><td>n/a</td></tr>
|
||||
<tr><td><a href="https://wg21.link/p0181r1">p0181r1</a></td><td>LWG</td><td>Ordered by Default</td><td>Oulu</td><td><i>Removed in Kona</i></td><td>n/a</td></tr>
|
||||
<tr><td><a href="https://wg21.link/p0209r2">p0209r2</a></td><td>LWG</td><td>make_from_tuple: apply for construction</td><td>Oulu</td><td>Complete</td><td>3.9</td></tr>
|
||||
<tr><td><a href="https://wg21.link/p0219r1">p0219r1</a></td><td>LWG</td><td>Relative Paths for Filesystem</td><td>Oulu</td><td></td><td></td></tr>
|
||||
<tr><td><a href="https://wg21.link/p0219r1">p0219r1</a></td><td>LWG</td><td>Relative Paths for Filesystem</td><td>Oulu</td><td>Complete</td><td>7.0</td></tr>
|
||||
<tr><td><a href="https://wg21.link/p0254r2">p0254r2</a></td><td>LWG</td><td>Integrating std::string_view and std::string</td><td>Oulu</td><td>Complete</td><td>4.0</td></tr>
|
||||
<tr><td><a href="https://wg21.link/p0258r2">p0258r2</a></td><td>LWG</td><td>has_unique_object_representations</td><td>Oulu</td><td>Complete</td><td>6.0</td></tr>
|
||||
<tr><td><a href="https://wg21.link/p0295r0">p0295r0</a></td><td>LWG</td><td>Adopt Selected Library Fundamentals V2 Components for C++17</td><td>Oulu</td><td>Complete</td><td>4.0</td></tr>
|
||||
@@ -153,7 +153,7 @@
|
||||
<tr><td><a href="https://wg21.link/P0433R2">P0433R2</a></td><td>LWG</td><td>Toward a resolution of US7 and US14: Integrating template deduction for class templates into the standard library</td><td>Kona</td><td><i>In progress</i></td><td>7.0</td></tr>
|
||||
<tr><td><a href="https://wg21.link/P0452R1">P0452R1</a></td><td>LWG</td><td>Unifying <numeric> Parallel Algorithms</td><td>Kona</td><td></td><td></td></tr>
|
||||
<tr><td><a href="https://wg21.link/P0467R2">P0467R2</a></td><td>LWG</td><td>Iterator Concerns for Parallel Algorithms</td><td>Kona</td><td></td><td></td></tr>
|
||||
<tr><td><a href="https://wg21.link/P0492R2">P0492R2</a></td><td>LWG</td><td>Proposed Resolution of C++17 National Body Comments for Filesystems</td><td>Kona</td><td></td><td></td></tr>
|
||||
<tr><td><a href="https://wg21.link/P0492R2">P0492R2</a></td><td>LWG</td><td>Proposed Resolution of C++17 National Body Comments for Filesystems</td><td>Kona</td><td>Complete</td><td>7.0</td></tr>
|
||||
<tr><td><a href="https://wg21.link/P0518R1">P0518R1</a></td><td>LWG</td><td>Allowing copies as arguments to function objects given to parallel algorithms in response to CH11</td><td>Kona</td><td></td><td></td></tr>
|
||||
<tr><td><a href="https://wg21.link/P0523R1">P0523R1</a></td><td>LWG</td><td>Wording for CH 10: Complexity of parallel algorithms</td><td>Kona</td><td></td><td></td></tr>
|
||||
<tr><td><a href="https://wg21.link/P0548R1">P0548R1</a></td><td>LWG</td><td>common_type and duration</td><td>Kona</td><td>Complete</td><td>5.0</td></tr>
|
||||
|
||||
@@ -164,7 +164,7 @@
|
||||
<tr><td><a href="https://wg21.link/LWG3015">3015</a></td><td><tt>copy_options::<i>unspecified</i></tt> underspecified</td><td>Jacksonville</td><td><i>Nothing to do</i></td></tr>
|
||||
<tr><td><a href="https://wg21.link/LWG3017">3017</a></td><td><tt>list splice</tt> functions should use <tt>addressof</tt></td><td>Jacksonville</td><td></td></tr>
|
||||
<tr><td><a href="https://wg21.link/LWG3020">3020</a></td><td>[networking.ts] Remove spurious nested <tt>value_type</tt> buffer sequence requirement</td><td>Jacksonville</td><td></td></tr>
|
||||
<tr><td><a href="https://wg21.link/LWG3026">3026</a></td><td><tt>filesystem::weakly_canonical</tt> still defined in terms of <tt>canonical(p, base)</tt></td><td>Jacksonville</td><td></td></tr>
|
||||
<tr><td><a href="https://wg21.link/LWG3026">3026</a></td><td><tt>filesystem::weakly_canonical</tt> still defined in terms of <tt>canonical(p, base)</tt></td><td>Jacksonville</td><td>Complete</td></tr>
|
||||
<tr><td><a href="https://wg21.link/LWG3030">3030</a></td><td>Who shall meet the requirements of <tt>try_lock</tt>?</td><td>Jacksonville</td><td><i>Nothing to do</i></td></tr>
|
||||
<tr><td><a href="https://wg21.link/LWG3034">3034</a></td><td>P0767R1 breaks previously-standard-layout types</td><td>Jacksonville</td><td>Complete</td></tr>
|
||||
<tr><td><a href="https://wg21.link/LWG3035">3035</a></td><td><tt>std::allocator</tt>'s constructors should be <tt>constexpr</tt></td><td>Jacksonville</td><td>Complete</td></tr>
|
||||
|
||||
Reference in New Issue
Block a user