[libc] Switch to using the generic `<gpuintrin.h>` implementations (#121810)
[llvm-project.git] / libcxx / src / filesystem / path.cpp
blob9f7dc54fdf156c07154b9a52d99759492bd609c5
1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include <__config>
10 #include <filesystem>
11 #include <vector>
13 #include "error.h"
14 #include "path_parser.h"
16 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
18 using detail::ErrorHandler;
19 using parser::createView;
20 using parser::PathParser;
21 using parser::string_view_t;
23 ///////////////////////////////////////////////////////////////////////////////
24 // path definitions
25 ///////////////////////////////////////////////////////////////////////////////
27 _LIBCPP_DIAGNOSTIC_PUSH
28 _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated")
29 constexpr path::value_type path::preferred_separator;
30 _LIBCPP_DIAGNOSTIC_POP
32 path& path::replace_extension(path const& replacement) {
33 path p = extension();
34 if (not p.empty()) {
35 __pn_.erase(__pn_.size() - p.native().size());
37 if (!replacement.empty()) {
38 if (replacement.native()[0] != '.') {
39 __pn_ += PATHSTR(".");
41 __pn_.append(replacement.__pn_);
43 return *this;
46 ///////////////////////////////////////////////////////////////////////////////
47 // path.decompose
49 string_view_t path::__root_name() const {
50 auto PP = PathParser::CreateBegin(__pn_);
51 if (PP.State_ == PathParser::PS_InRootName)
52 return *PP;
53 return {};
56 string_view_t path::__root_directory() const {
57 auto PP = PathParser::CreateBegin(__pn_);
58 if (PP.State_ == PathParser::PS_InRootName)
59 ++PP;
60 if (PP.State_ == PathParser::PS_InRootDir)
61 return *PP;
62 return {};
65 string_view_t path::__root_path_raw() const {
66 auto PP = PathParser::CreateBegin(__pn_);
67 if (PP.State_ == PathParser::PS_InRootName) {
68 auto NextCh = PP.peek();
69 if (NextCh && isSeparator(*NextCh)) {
70 ++PP;
71 return createView(__pn_.data(), &PP.RawEntry.back());
73 return PP.RawEntry;
75 if (PP.State_ == PathParser::PS_InRootDir)
76 return *PP;
77 return {};
80 static bool ConsumeRootName(PathParser* PP) {
81 static_assert(PathParser::PS_BeforeBegin == 1 && PathParser::PS_InRootName == 2, "Values for enums are incorrect");
82 while (PP->State_ <= PathParser::PS_InRootName)
83 ++(*PP);
84 return PP->State_ == PathParser::PS_AtEnd;
87 static bool ConsumeRootDir(PathParser* PP) {
88 static_assert(PathParser::PS_BeforeBegin == 1 && PathParser::PS_InRootName == 2 && PathParser::PS_InRootDir == 3,
89 "Values for enums are incorrect");
90 while (PP->State_ <= PathParser::PS_InRootDir)
91 ++(*PP);
92 return PP->State_ == PathParser::PS_AtEnd;
95 string_view_t path::__relative_path() const {
96 auto PP = PathParser::CreateBegin(__pn_);
97 if (ConsumeRootDir(&PP))
98 return {};
99 return createView(PP.RawEntry.data(), &__pn_.back());
102 string_view_t path::__parent_path() const {
103 if (empty())
104 return {};
105 // Determine if we have a root path but not a relative path. In that case
106 // return *this.
108 auto PP = PathParser::CreateBegin(__pn_);
109 if (ConsumeRootDir(&PP))
110 return __pn_;
112 // Otherwise remove a single element from the end of the path, and return
113 // a string representing that path
115 auto PP = PathParser::CreateEnd(__pn_);
116 --PP;
117 if (PP.RawEntry.data() == __pn_.data())
118 return {};
119 --PP;
120 return createView(__pn_.data(), &PP.RawEntry.back());
124 string_view_t path::__filename() const {
125 if (empty())
126 return {};
128 PathParser PP = PathParser::CreateBegin(__pn_);
129 if (ConsumeRootDir(&PP))
130 return {};
132 return *(--PathParser::CreateEnd(__pn_));
135 string_view_t path::__stem() const { return parser::separate_filename(__filename()).first; }
137 string_view_t path::__extension() const { return parser::separate_filename(__filename()).second; }
139 ////////////////////////////////////////////////////////////////////////////
140 // path.gen
142 enum PathPartKind : unsigned char { PK_None, PK_RootSep, PK_Filename, PK_Dot, PK_DotDot, PK_TrailingSep };
144 static PathPartKind ClassifyPathPart(string_view_t Part) {
145 if (Part.empty())
146 return PK_TrailingSep;
147 if (Part == PATHSTR("."))
148 return PK_Dot;
149 if (Part == PATHSTR(".."))
150 return PK_DotDot;
151 if (Part == PATHSTR("/"))
152 return PK_RootSep;
153 #if defined(_LIBCPP_WIN32API)
154 if (Part == PATHSTR("\\"))
155 return PK_RootSep;
156 #endif
157 return PK_Filename;
160 path path::lexically_normal() const {
161 if (__pn_.empty())
162 return *this;
164 using PartKindPair = pair<string_view_t, PathPartKind>;
165 vector<PartKindPair> Parts;
166 // Guess as to how many elements the path has to avoid reallocating.
167 Parts.reserve(32);
169 // Track the total size of the parts as we collect them. This allows the
170 // resulting path to reserve the correct amount of memory.
171 size_t NewPathSize = 0;
172 auto AddPart = [&](PathPartKind K, string_view_t P) {
173 NewPathSize += P.size();
174 Parts.emplace_back(P, K);
176 auto LastPartKind = [&]() {
177 if (Parts.empty())
178 return PK_None;
179 return Parts.back().second;
182 bool MaybeNeedTrailingSep = false;
183 // Build a stack containing the remaining elements of the path, popping off
184 // elements which occur before a '..' entry.
185 for (auto PP = PathParser::CreateBegin(__pn_); PP; ++PP) {
186 auto Part = *PP;
187 PathPartKind Kind = ClassifyPathPart(Part);
188 switch (Kind) {
189 case PK_Filename:
190 case PK_RootSep: {
191 // Add all non-dot and non-dot-dot elements to the stack of elements.
192 AddPart(Kind, Part);
193 MaybeNeedTrailingSep = false;
194 break;
196 case PK_DotDot: {
197 // Only push a ".." element if there are no elements preceding the "..",
198 // or if the preceding element is itself "..".
199 auto LastKind = LastPartKind();
200 if (LastKind == PK_Filename) {
201 NewPathSize -= Parts.back().first.size();
202 Parts.pop_back();
203 } else if (LastKind != PK_RootSep)
204 AddPart(PK_DotDot, PATHSTR(".."));
205 MaybeNeedTrailingSep = LastKind == PK_Filename;
206 break;
208 case PK_Dot:
209 case PK_TrailingSep: {
210 MaybeNeedTrailingSep = true;
211 break;
213 case PK_None:
214 __libcpp_unreachable();
217 // [fs.path.generic]p6.8: If the path is empty, add a dot.
218 if (Parts.empty())
219 return PATHSTR(".");
221 // [fs.path.generic]p6.7: If the last filename is dot-dot, remove any
222 // trailing directory-separator.
223 bool NeedTrailingSep = MaybeNeedTrailingSep && LastPartKind() == PK_Filename;
225 path Result;
226 Result.__pn_.reserve(Parts.size() + NewPathSize + NeedTrailingSep);
227 for (auto& PK : Parts)
228 Result /= PK.first;
230 if (NeedTrailingSep)
231 Result /= PATHSTR("");
233 Result.make_preferred();
234 return Result;
237 static int DetermineLexicalElementCount(PathParser PP) {
238 int Count = 0;
239 for (; PP; ++PP) {
240 auto Elem = *PP;
241 if (Elem == PATHSTR(".."))
242 --Count;
243 else if (Elem != PATHSTR(".") && Elem != PATHSTR(""))
244 ++Count;
246 return Count;
249 path path::lexically_relative(const path& base) const {
250 { // perform root-name/root-directory mismatch checks
251 auto PP = PathParser::CreateBegin(__pn_);
252 auto PPBase = PathParser::CreateBegin(base.__pn_);
253 auto CheckIterMismatchAtBase = [&]() {
254 return PP.State_ != PPBase.State_ && (PP.inRootPath() || PPBase.inRootPath());
256 if (PP.inRootName() && PPBase.inRootName()) {
257 if (*PP != *PPBase)
258 return {};
259 } else if (CheckIterMismatchAtBase())
260 return {};
262 if (PP.inRootPath())
263 ++PP;
264 if (PPBase.inRootPath())
265 ++PPBase;
266 if (CheckIterMismatchAtBase())
267 return {};
270 // Find the first mismatching element
271 auto PP = PathParser::CreateBegin(__pn_);
272 auto PPBase = PathParser::CreateBegin(base.__pn_);
273 while (PP && PPBase && PP.State_ == PPBase.State_ && (*PP == *PPBase || PP.inRootDir())) {
274 ++PP;
275 ++PPBase;
278 // If there is no mismatch, return ".".
279 if (!PP && !PPBase)
280 return ".";
282 // Otherwise, determine the number of elements, 'n', which are not dot or
283 // dot-dot minus the number of dot-dot elements.
284 int ElemCount = DetermineLexicalElementCount(PPBase);
285 if (ElemCount < 0)
286 return {};
288 // if n == 0 and (a == end() || a->empty()), returns path("."); otherwise
289 if (ElemCount == 0 && (PP.atEnd() || *PP == PATHSTR("")))
290 return PATHSTR(".");
292 // return a path constructed with 'n' dot-dot elements, followed by the
293 // elements of '*this' after the mismatch.
294 path Result;
295 // FIXME: Reserve enough room in Result that it won't have to re-allocate.
296 while (ElemCount--)
297 Result /= PATHSTR("..");
298 for (; PP; ++PP)
299 Result /= *PP;
300 return Result;
303 ////////////////////////////////////////////////////////////////////////////
304 // path.comparisons
305 static int CompareRootName(PathParser* LHS, PathParser* RHS) {
306 if (!LHS->inRootName() && !RHS->inRootName())
307 return 0;
309 auto GetRootName = [](PathParser* Parser) -> string_view_t { return Parser->inRootName() ? **Parser : PATHSTR(""); };
310 int res = GetRootName(LHS).compare(GetRootName(RHS));
311 ConsumeRootName(LHS);
312 ConsumeRootName(RHS);
313 return res;
316 static int CompareRootDir(PathParser* LHS, PathParser* RHS) {
317 if (!LHS->inRootDir() && RHS->inRootDir())
318 return -1;
319 else if (LHS->inRootDir() && !RHS->inRootDir())
320 return 1;
321 else {
322 ConsumeRootDir(LHS);
323 ConsumeRootDir(RHS);
324 return 0;
328 static int CompareRelative(PathParser* LHSPtr, PathParser* RHSPtr) {
329 auto& LHS = *LHSPtr;
330 auto& RHS = *RHSPtr;
332 int res;
333 while (LHS && RHS) {
334 if ((res = (*LHS).compare(*RHS)) != 0)
335 return res;
336 ++LHS;
337 ++RHS;
339 return 0;
342 static int CompareEndState(PathParser* LHS, PathParser* RHS) {
343 if (LHS->atEnd() && !RHS->atEnd())
344 return -1;
345 else if (!LHS->atEnd() && RHS->atEnd())
346 return 1;
347 return 0;
350 int path::__compare(string_view_t __s) const {
351 auto LHS = PathParser::CreateBegin(__pn_);
352 auto RHS = PathParser::CreateBegin(__s);
353 int res;
355 if ((res = CompareRootName(&LHS, &RHS)) != 0)
356 return res;
358 if ((res = CompareRootDir(&LHS, &RHS)) != 0)
359 return res;
361 if ((res = CompareRelative(&LHS, &RHS)) != 0)
362 return res;
364 return CompareEndState(&LHS, &RHS);
367 ////////////////////////////////////////////////////////////////////////////
368 // path.nonmembers
369 size_t hash_value(const path& __p) noexcept {
370 auto PP = PathParser::CreateBegin(__p.native());
371 size_t hash_value = 0;
372 hash<string_view_t> hasher;
373 while (PP) {
374 string_view_t Part = PP.inRootDir() ? PATHSTR("/") : *PP;
375 hash_value = __hash_combine(hash_value, hasher(Part));
376 ++PP;
378 return hash_value;
381 ////////////////////////////////////////////////////////////////////////////
382 // path.itr
383 path::iterator path::begin() const {
384 auto PP = PathParser::CreateBegin(__pn_);
385 iterator it;
386 it.__path_ptr_ = this;
387 it.__state_ = static_cast<path::iterator::_ParserState>(PP.State_);
388 it.__entry_ = PP.RawEntry;
389 it.__stashed_elem_.__assign_view(*PP);
390 return it;
393 path::iterator path::end() const {
394 iterator it{};
395 it.__state_ = path::iterator::_AtEnd;
396 it.__path_ptr_ = this;
397 return it;
400 path::iterator& path::iterator::__increment() {
401 PathParser PP(__path_ptr_->native(), __entry_, __state_);
402 ++PP;
403 __state_ = static_cast<_ParserState>(PP.State_);
404 __entry_ = PP.RawEntry;
405 __stashed_elem_.__assign_view(*PP);
406 return *this;
409 path::iterator& path::iterator::__decrement() {
410 PathParser PP(__path_ptr_->native(), __entry_, __state_);
411 --PP;
412 __state_ = static_cast<_ParserState>(PP.State_);
413 __entry_ = PP.RawEntry;
414 __stashed_elem_.__assign_view(*PP);
415 return *this;
418 #if defined(_LIBCPP_WIN32API)
419 ////////////////////////////////////////////////////////////////////////////
420 // Windows path conversions
421 size_t __wide_to_char(const wstring& str, char* out, size_t outlen) {
422 if (str.empty())
423 return 0;
424 ErrorHandler<size_t> err("__wide_to_char", nullptr);
425 UINT codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
426 BOOL used_default = FALSE;
427 int ret = WideCharToMultiByte(codepage, 0, str.data(), str.size(), out, outlen, nullptr, &used_default);
428 if (ret <= 0 || used_default)
429 return err.report(errc::illegal_byte_sequence);
430 return ret;
433 size_t __char_to_wide(const string& str, wchar_t* out, size_t outlen) {
434 if (str.empty())
435 return 0;
436 ErrorHandler<size_t> err("__char_to_wide", nullptr);
437 UINT codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
438 int ret = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, str.data(), str.size(), out, outlen);
439 if (ret <= 0)
440 return err.report(errc::illegal_byte_sequence);
441 return ret;
443 #endif
445 _LIBCPP_END_NAMESPACE_FILESYSTEM