Buckets:
arudradey/ml-cpu-storage / emsdk /upstream /emscripten /system /lib /libcxx /src /filesystem /path.cpp
| //===----------------------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM | |
| using detail::ErrorHandler; | |
| using parser::createView; | |
| using parser::PathParser; | |
| using parser::string_view_t; | |
| /////////////////////////////////////////////////////////////////////////////// | |
| // path definitions | |
| /////////////////////////////////////////////////////////////////////////////// | |
| _LIBCPP_DIAGNOSTIC_PUSH | |
| _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated") | |
| constexpr path::value_type path::preferred_separator; | |
| _LIBCPP_DIAGNOSTIC_POP | |
| 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_ += PATHSTR("."); | |
| } | |
| __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 && isSeparator(*NextCh)) { | |
| ++PP; | |
| return createView(__pn_.data(), &PP.RawEntry.back()); | |
| } | |
| return PP.RawEntry; | |
| } | |
| if (PP.State_ == PathParser::PS_InRootDir) | |
| return *PP; | |
| return {}; | |
| } | |
| static bool ConsumeRootName(PathParser* PP) { | |
| static_assert(PathParser::PS_BeforeBegin == 1 && PathParser::PS_InRootName == 2, "Values for enums are incorrect"); | |
| while (PP->State_ <= PathParser::PS_InRootName) | |
| ++(*PP); | |
| return PP->State_ == PathParser::PS_AtEnd; | |
| } | |
| static bool ConsumeRootDir(PathParser* PP) { | |
| static_assert(PathParser::PS_BeforeBegin == 1 && PathParser::PS_InRootName == 2 && PathParser::PS_InRootDir == 3, | |
| "Values for enums are incorrect"); | |
| 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 == PATHSTR(".")) | |
| return PK_Dot; | |
| if (Part == PATHSTR("..")) | |
| return PK_DotDot; | |
| if (Part == PATHSTR("/")) | |
| return PK_RootSep; | |
| if (Part == PATHSTR("\\")) | |
| return PK_RootSep; | |
| return PK_Filename; | |
| } | |
| path path::lexically_normal() const { | |
| if (__pn_.empty()) | |
| return *this; | |
| using PartKindPair = pair<string_view_t, PathPartKind>; | |
| 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, PATHSTR("..")); | |
| 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 PATHSTR("."); | |
| // [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 /= PATHSTR(""); | |
| Result.make_preferred(); | |
| return Result; | |
| } | |
| static int DetermineLexicalElementCount(PathParser PP) { | |
| int Count = 0; | |
| for (; PP; ++PP) { | |
| auto Elem = *PP; | |
| if (Elem == PATHSTR("..")) | |
| --Count; | |
| else if (Elem != PATHSTR(".") && Elem != PATHSTR("")) | |
| ++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.inRootName() && PPBase.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.inRootDir())) { | |
| ++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 {}; | |
| // if n == 0 and (a == end() || a->empty()), returns path("."); otherwise | |
| if (ElemCount == 0 && (PP.atEnd() || *PP == PATHSTR(""))) | |
| return PATHSTR("."); | |
| // return a path constructed with 'n' dot-dot elements, followed by 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 /= PATHSTR(".."); | |
| for (; PP; ++PP) | |
| Result /= *PP; | |
| return Result; | |
| } | |
| //////////////////////////////////////////////////////////////////////////// | |
| // path.comparisons | |
| static int CompareRootName(PathParser* LHS, PathParser* RHS) { | |
| if (!LHS->inRootName() && !RHS->inRootName()) | |
| return 0; | |
| auto GetRootName = [](PathParser* Parser) -> string_view_t { return Parser->inRootName() ? **Parser : PATHSTR(""); }; | |
| int res = GetRootName(LHS).compare(GetRootName(RHS)); | |
| ConsumeRootName(LHS); | |
| ConsumeRootName(RHS); | |
| return res; | |
| } | |
| static int CompareRootDir(PathParser* LHS, PathParser* RHS) { | |
| if (!LHS->inRootDir() && RHS->inRootDir()) | |
| return -1; | |
| else if (LHS->inRootDir() && !RHS->inRootDir()) | |
| return 1; | |
| else { | |
| ConsumeRootDir(LHS); | |
| ConsumeRootDir(RHS); | |
| return 0; | |
| } | |
| } | |
| static int CompareRelative(PathParser* LHSPtr, PathParser* RHSPtr) { | |
| auto& LHS = *LHSPtr; | |
| auto& RHS = *RHSPtr; | |
| int res; | |
| while (LHS && RHS) { | |
| if ((res = (*LHS).compare(*RHS)) != 0) | |
| return res; | |
| ++LHS; | |
| ++RHS; | |
| } | |
| return 0; | |
| } | |
| static int CompareEndState(PathParser* LHS, PathParser* RHS) { | |
| if (LHS->atEnd() && !RHS->atEnd()) | |
| return -1; | |
| else if (!LHS->atEnd() && RHS->atEnd()) | |
| return 1; | |
| return 0; | |
| } | |
| int path::__compare(string_view_t __s) const { | |
| auto LHS = PathParser::CreateBegin(__pn_); | |
| auto RHS = PathParser::CreateBegin(__s); | |
| int res; | |
| if ((res = CompareRootName(&LHS, &RHS)) != 0) | |
| return res; | |
| if ((res = CompareRootDir(&LHS, &RHS)) != 0) | |
| return res; | |
| if ((res = CompareRelative(&LHS, &RHS)) != 0) | |
| return res; | |
| return CompareEndState(&LHS, &RHS); | |
| } | |
| //////////////////////////////////////////////////////////////////////////// | |
| // path.nonmembers | |
| size_t hash_value(const path& __p) noexcept { | |
| auto PP = PathParser::CreateBegin(__p.native()); | |
| size_t hash_value = 0; | |
| hash<string_view_t> hasher; | |
| while (PP) { | |
| string_view_t Part = PP.inRootDir() ? PATHSTR("/") : *PP; | |
| hash_value = __hash_combine(hash_value, hasher(Part)); | |
| ++PP; | |
| } | |
| return hash_value; | |
| } | |
| //////////////////////////////////////////////////////////////////////////// | |
| // path.itr | |
| path::iterator path::begin() const { | |
| auto PP = PathParser::CreateBegin(__pn_); | |
| iterator it; | |
| it.__path_ptr_ = this; | |
| it.__state_ = static_cast<path::iterator::_ParserState>(PP.State_); | |
| it.__entry_ = PP.RawEntry; | |
| it.__stashed_elem_.__assign_view(*PP); | |
| return it; | |
| } | |
| path::iterator path::end() const { | |
| iterator it{}; | |
| it.__state_ = path::iterator::_AtEnd; | |
| it.__path_ptr_ = this; | |
| return it; | |
| } | |
| path::iterator& path::iterator::__increment() { | |
| PathParser PP(__path_ptr_->native(), __entry_, __state_); | |
| ++PP; | |
| __state_ = static_cast<_ParserState>(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_ = static_cast<_ParserState>(PP.State_); | |
| __entry_ = PP.RawEntry; | |
| __stashed_elem_.__assign_view(*PP); | |
| return *this; | |
| } | |
| //////////////////////////////////////////////////////////////////////////// | |
| // Windows path conversions | |
| size_t __wide_to_char(const wstring& str, char* out, size_t outlen) { | |
| if (str.empty()) | |
| return 0; | |
| ErrorHandler<size_t> err("__wide_to_char", nullptr); | |
| UINT codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; | |
| BOOL used_default = FALSE; | |
| int ret = WideCharToMultiByte(codepage, 0, str.data(), str.size(), out, outlen, nullptr, &used_default); | |
| if (ret <= 0 || used_default) | |
| return err.report(errc::illegal_byte_sequence); | |
| return ret; | |
| } | |
| size_t __char_to_wide(const string& str, wchar_t* out, size_t outlen) { | |
| if (str.empty()) | |
| return 0; | |
| ErrorHandler<size_t> err("__char_to_wide", nullptr); | |
| UINT codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; | |
| int ret = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, str.data(), str.size(), out, outlen); | |
| if (ret <= 0) | |
| return err.report(errc::illegal_byte_sequence); | |
| return ret; | |
| } | |
| _LIBCPP_END_NAMESPACE_FILESYSTEM | |
Xet Storage Details
- Size:
- 12.9 kB
- Xet hash:
- 158c6a406ba60badadc35122d851790f11d5b97a8580ccd5e40856aa017bee23
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.