xref: /llvm-project/libcxx/src/filesystem/path_parser.h (revision 731db06a878f5c8cb29b36d526a54493677ea89f)
1c7d3c844SLouis Dionne //===----------------------------------------------------------------------===//
2c7d3c844SLouis Dionne //
3c7d3c844SLouis Dionne // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c7d3c844SLouis Dionne // See https://llvm.org/LICENSE.txt for license information.
5c7d3c844SLouis Dionne // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c7d3c844SLouis Dionne //
7c7d3c844SLouis Dionne //===----------------------------------------------------------------------===//
8c7d3c844SLouis Dionne 
9c7d3c844SLouis Dionne #ifndef PATH_PARSER_H
10c7d3c844SLouis Dionne #define PATH_PARSER_H
11c7d3c844SLouis Dionne 
12c7d3c844SLouis Dionne #include <__config>
13c7d3c844SLouis Dionne #include <__utility/unreachable.h>
14c7d3c844SLouis Dionne #include <cstddef>
15c7d3c844SLouis Dionne #include <filesystem>
16c7d3c844SLouis Dionne #include <utility>
17c7d3c844SLouis Dionne 
18c7d3c844SLouis Dionne #include "format_string.h"
19c7d3c844SLouis Dionne 
20c7d3c844SLouis Dionne _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
21c7d3c844SLouis Dionne 
isSeparator(path::value_type C)2221853b96SLouis Dionne inline bool isSeparator(path::value_type C) {
23c7d3c844SLouis Dionne   if (C == '/')
24c7d3c844SLouis Dionne     return true;
25c7d3c844SLouis Dionne #if defined(_LIBCPP_WIN32API)
26c7d3c844SLouis Dionne   if (C == '\\')
27c7d3c844SLouis Dionne     return true;
28c7d3c844SLouis Dionne #endif
29c7d3c844SLouis Dionne   return false;
30c7d3c844SLouis Dionne }
31c7d3c844SLouis Dionne 
isDriveLetter(path::value_type C)329783f28cSLouis Dionne inline bool isDriveLetter(path::value_type C) { return (C >= 'a' && C <= 'z') || (C >= 'A' && C <= 'Z'); }
33c7d3c844SLouis Dionne 
34c7d3c844SLouis Dionne namespace parser {
35c7d3c844SLouis Dionne 
36c7d3c844SLouis Dionne using string_view_t    = path::__string_view;
37c7d3c844SLouis Dionne using string_view_pair = pair<string_view_t, string_view_t>;
38c7d3c844SLouis Dionne using PosPtr           = path::value_type const*;
39c7d3c844SLouis Dionne 
40c7d3c844SLouis Dionne struct PathParser {
41c7d3c844SLouis Dionne   enum ParserState : unsigned char {
42c7d3c844SLouis Dionne     // Zero is a special sentinel value used by default constructed iterators.
43c7d3c844SLouis Dionne     PS_BeforeBegin   = path::iterator::_BeforeBegin,
44c7d3c844SLouis Dionne     PS_InRootName    = path::iterator::_InRootName,
45c7d3c844SLouis Dionne     PS_InRootDir     = path::iterator::_InRootDir,
46c7d3c844SLouis Dionne     PS_InFilenames   = path::iterator::_InFilenames,
47c7d3c844SLouis Dionne     PS_InTrailingSep = path::iterator::_InTrailingSep,
48c7d3c844SLouis Dionne     PS_AtEnd         = path::iterator::_AtEnd
49c7d3c844SLouis Dionne   };
50c7d3c844SLouis Dionne 
51c7d3c844SLouis Dionne   const string_view_t Path;
52c7d3c844SLouis Dionne   string_view_t RawEntry;
53*731db06aSNikolas Klauser   ParserState State_;
54c7d3c844SLouis Dionne 
55c7d3c844SLouis Dionne private:
PathParserPathParser56*731db06aSNikolas Klauser   PathParser(string_view_t P, ParserState State) noexcept : Path(P), State_(State) {}
57c7d3c844SLouis Dionne 
58c7d3c844SLouis Dionne public:
PathParserPathParser59c7d3c844SLouis Dionne   PathParser(string_view_t P, string_view_t E, unsigned char S)
60*731db06aSNikolas Klauser       : Path(P), RawEntry(E), State_(static_cast<ParserState>(S)) {
61c7d3c844SLouis Dionne     // S cannot be '0' or PS_BeforeBegin.
62c7d3c844SLouis Dionne   }
63c7d3c844SLouis Dionne 
CreateBeginPathParser64c7d3c844SLouis Dionne   static PathParser CreateBegin(string_view_t P) noexcept {
65c7d3c844SLouis Dionne     PathParser PP(P, PS_BeforeBegin);
66c7d3c844SLouis Dionne     PP.increment();
67c7d3c844SLouis Dionne     return PP;
68c7d3c844SLouis Dionne   }
69c7d3c844SLouis Dionne 
CreateEndPathParser70c7d3c844SLouis Dionne   static PathParser CreateEnd(string_view_t P) noexcept {
71c7d3c844SLouis Dionne     PathParser PP(P, PS_AtEnd);
72c7d3c844SLouis Dionne     return PP;
73c7d3c844SLouis Dionne   }
74c7d3c844SLouis Dionne 
peekPathParser75c7d3c844SLouis Dionne   PosPtr peek() const noexcept {
76c7d3c844SLouis Dionne     auto TkEnd = getNextTokenStartPos();
77c7d3c844SLouis Dionne     auto End   = getAfterBack();
78c7d3c844SLouis Dionne     return TkEnd == End ? nullptr : TkEnd;
79c7d3c844SLouis Dionne   }
80c7d3c844SLouis Dionne 
incrementPathParser81c7d3c844SLouis Dionne   void increment() noexcept {
82c7d3c844SLouis Dionne     const PosPtr End   = getAfterBack();
83c7d3c844SLouis Dionne     const PosPtr Start = getNextTokenStartPos();
84c7d3c844SLouis Dionne     if (Start == End)
85c7d3c844SLouis Dionne       return makeState(PS_AtEnd);
86c7d3c844SLouis Dionne 
87*731db06aSNikolas Klauser     switch (State_) {
88c7d3c844SLouis Dionne     case PS_BeforeBegin: {
89c7d3c844SLouis Dionne       PosPtr TkEnd = consumeRootName(Start, End);
90c7d3c844SLouis Dionne       if (TkEnd)
91c7d3c844SLouis Dionne         return makeState(PS_InRootName, Start, TkEnd);
92c7d3c844SLouis Dionne     }
93c7d3c844SLouis Dionne       _LIBCPP_FALLTHROUGH();
94c7d3c844SLouis Dionne     case PS_InRootName: {
95c7d3c844SLouis Dionne       PosPtr TkEnd = consumeAllSeparators(Start, End);
96c7d3c844SLouis Dionne       if (TkEnd)
97c7d3c844SLouis Dionne         return makeState(PS_InRootDir, Start, TkEnd);
98c7d3c844SLouis Dionne       else
99c7d3c844SLouis Dionne         return makeState(PS_InFilenames, Start, consumeName(Start, End));
100c7d3c844SLouis Dionne     }
101c7d3c844SLouis Dionne     case PS_InRootDir:
102c7d3c844SLouis Dionne       return makeState(PS_InFilenames, Start, consumeName(Start, End));
103c7d3c844SLouis Dionne 
104c7d3c844SLouis Dionne     case PS_InFilenames: {
105c7d3c844SLouis Dionne       PosPtr SepEnd = consumeAllSeparators(Start, End);
106c7d3c844SLouis Dionne       if (SepEnd != End) {
107c7d3c844SLouis Dionne         PosPtr TkEnd = consumeName(SepEnd, End);
108c7d3c844SLouis Dionne         if (TkEnd)
109c7d3c844SLouis Dionne           return makeState(PS_InFilenames, SepEnd, TkEnd);
110c7d3c844SLouis Dionne       }
111c7d3c844SLouis Dionne       return makeState(PS_InTrailingSep, Start, SepEnd);
112c7d3c844SLouis Dionne     }
113c7d3c844SLouis Dionne 
114c7d3c844SLouis Dionne     case PS_InTrailingSep:
115c7d3c844SLouis Dionne       return makeState(PS_AtEnd);
116c7d3c844SLouis Dionne 
117c7d3c844SLouis Dionne     case PS_AtEnd:
118c7d3c844SLouis Dionne       __libcpp_unreachable();
119c7d3c844SLouis Dionne     }
120c7d3c844SLouis Dionne   }
121c7d3c844SLouis Dionne 
decrementPathParser122c7d3c844SLouis Dionne   void decrement() noexcept {
123c7d3c844SLouis Dionne     const PosPtr REnd   = getBeforeFront();
124c7d3c844SLouis Dionne     const PosPtr RStart = getCurrentTokenStartPos() - 1;
125c7d3c844SLouis Dionne     if (RStart == REnd) // we're decrementing the begin
126c7d3c844SLouis Dionne       return makeState(PS_BeforeBegin);
127c7d3c844SLouis Dionne 
128*731db06aSNikolas Klauser     switch (State_) {
129c7d3c844SLouis Dionne     case PS_AtEnd: {
130c7d3c844SLouis Dionne       // Try to consume a trailing separator or root directory first.
131c7d3c844SLouis Dionne       if (PosPtr SepEnd = consumeAllSeparators(RStart, REnd)) {
132c7d3c844SLouis Dionne         if (SepEnd == REnd)
133c7d3c844SLouis Dionne           return makeState(PS_InRootDir, Path.data(), RStart + 1);
134c7d3c844SLouis Dionne         PosPtr TkStart = consumeRootName(SepEnd, REnd);
135c7d3c844SLouis Dionne         if (TkStart == REnd)
136c7d3c844SLouis Dionne           return makeState(PS_InRootDir, RStart, RStart + 1);
137c7d3c844SLouis Dionne         return makeState(PS_InTrailingSep, SepEnd + 1, RStart + 1);
138c7d3c844SLouis Dionne       } else {
139c7d3c844SLouis Dionne         PosPtr TkStart = consumeRootName(RStart, REnd);
140c7d3c844SLouis Dionne         if (TkStart == REnd)
141c7d3c844SLouis Dionne           return makeState(PS_InRootName, TkStart + 1, RStart + 1);
142c7d3c844SLouis Dionne         TkStart = consumeName(RStart, REnd);
143c7d3c844SLouis Dionne         return makeState(PS_InFilenames, TkStart + 1, RStart + 1);
144c7d3c844SLouis Dionne       }
145c7d3c844SLouis Dionne     }
146c7d3c844SLouis Dionne     case PS_InTrailingSep:
1479783f28cSLouis Dionne       return makeState(PS_InFilenames, consumeName(RStart, REnd) + 1, RStart + 1);
148c7d3c844SLouis Dionne     case PS_InFilenames: {
149c7d3c844SLouis Dionne       PosPtr SepEnd = consumeAllSeparators(RStart, REnd);
150c7d3c844SLouis Dionne       if (SepEnd == REnd)
151c7d3c844SLouis Dionne         return makeState(PS_InRootDir, Path.data(), RStart + 1);
152c7d3c844SLouis Dionne       PosPtr TkStart = consumeRootName(SepEnd ? SepEnd : RStart, REnd);
153c7d3c844SLouis Dionne       if (TkStart == REnd) {
154c7d3c844SLouis Dionne         if (SepEnd)
155c7d3c844SLouis Dionne           return makeState(PS_InRootDir, SepEnd + 1, RStart + 1);
156c7d3c844SLouis Dionne         return makeState(PS_InRootName, TkStart + 1, RStart + 1);
157c7d3c844SLouis Dionne       }
158c7d3c844SLouis Dionne       TkStart = consumeName(SepEnd, REnd);
159c7d3c844SLouis Dionne       return makeState(PS_InFilenames, TkStart + 1, SepEnd + 1);
160c7d3c844SLouis Dionne     }
161c7d3c844SLouis Dionne     case PS_InRootDir:
162c7d3c844SLouis Dionne       return makeState(PS_InRootName, Path.data(), RStart + 1);
163c7d3c844SLouis Dionne     case PS_InRootName:
164c7d3c844SLouis Dionne     case PS_BeforeBegin:
165c7d3c844SLouis Dionne       __libcpp_unreachable();
166c7d3c844SLouis Dionne     }
167c7d3c844SLouis Dionne   }
168c7d3c844SLouis Dionne 
169c7d3c844SLouis Dionne   /// \brief Return a view with the "preferred representation" of the current
170c7d3c844SLouis Dionne   ///   element. For example trailing separators are represented as a '.'
171c7d3c844SLouis Dionne   string_view_t operator*() const noexcept {
172*731db06aSNikolas Klauser     switch (State_) {
173c7d3c844SLouis Dionne     case PS_BeforeBegin:
174c7d3c844SLouis Dionne     case PS_AtEnd:
175c7d3c844SLouis Dionne       return PATHSTR("");
176c7d3c844SLouis Dionne     case PS_InRootDir:
177c7d3c844SLouis Dionne       if (RawEntry[0] == '\\')
178c7d3c844SLouis Dionne         return PATHSTR("\\");
179c7d3c844SLouis Dionne       else
180c7d3c844SLouis Dionne         return PATHSTR("/");
181c7d3c844SLouis Dionne     case PS_InTrailingSep:
182c7d3c844SLouis Dionne       return PATHSTR("");
183c7d3c844SLouis Dionne     case PS_InRootName:
184c7d3c844SLouis Dionne     case PS_InFilenames:
185c7d3c844SLouis Dionne       return RawEntry;
186c7d3c844SLouis Dionne     }
187c7d3c844SLouis Dionne     __libcpp_unreachable();
188c7d3c844SLouis Dionne   }
189c7d3c844SLouis Dionne 
190*731db06aSNikolas Klauser   explicit operator bool() const noexcept { return State_ != PS_BeforeBegin && State_ != PS_AtEnd; }
191c7d3c844SLouis Dionne 
192c7d3c844SLouis Dionne   PathParser& operator++() noexcept {
193c7d3c844SLouis Dionne     increment();
194c7d3c844SLouis Dionne     return *this;
195c7d3c844SLouis Dionne   }
196c7d3c844SLouis Dionne 
197c7d3c844SLouis Dionne   PathParser& operator--() noexcept {
198c7d3c844SLouis Dionne     decrement();
199c7d3c844SLouis Dionne     return *this;
200c7d3c844SLouis Dionne   }
201c7d3c844SLouis Dionne 
atEndPathParser202*731db06aSNikolas Klauser   bool atEnd() const noexcept { return State_ == PS_AtEnd; }
203c7d3c844SLouis Dionne 
inRootDirPathParser204*731db06aSNikolas Klauser   bool inRootDir() const noexcept { return State_ == PS_InRootDir; }
205c7d3c844SLouis Dionne 
inRootNamePathParser206*731db06aSNikolas Klauser   bool inRootName() const noexcept { return State_ == PS_InRootName; }
207c7d3c844SLouis Dionne 
inRootPathPathParser2089783f28cSLouis Dionne   bool inRootPath() const noexcept { return inRootName() || inRootDir(); }
209c7d3c844SLouis Dionne 
210c7d3c844SLouis Dionne private:
makeStatePathParser211c7d3c844SLouis Dionne   void makeState(ParserState NewState, PosPtr Start, PosPtr End) noexcept {
212*731db06aSNikolas Klauser     State_    = NewState;
213c7d3c844SLouis Dionne     RawEntry = string_view_t(Start, End - Start);
214c7d3c844SLouis Dionne   }
makeStatePathParser215c7d3c844SLouis Dionne   void makeState(ParserState NewState) noexcept {
216*731db06aSNikolas Klauser     State_    = NewState;
217c7d3c844SLouis Dionne     RawEntry = {};
218c7d3c844SLouis Dionne   }
219c7d3c844SLouis Dionne 
getAfterBackPathParser220c7d3c844SLouis Dionne   PosPtr getAfterBack() const noexcept { return Path.data() + Path.size(); }
221c7d3c844SLouis Dionne 
getBeforeFrontPathParser222c7d3c844SLouis Dionne   PosPtr getBeforeFront() const noexcept { return Path.data() - 1; }
223c7d3c844SLouis Dionne 
224c7d3c844SLouis Dionne   /// \brief Return a pointer to the first character after the currently
225c7d3c844SLouis Dionne   ///   lexed element.
getNextTokenStartPosPathParser226c7d3c844SLouis Dionne   PosPtr getNextTokenStartPos() const noexcept {
227*731db06aSNikolas Klauser     switch (State_) {
228c7d3c844SLouis Dionne     case PS_BeforeBegin:
229c7d3c844SLouis Dionne       return Path.data();
230c7d3c844SLouis Dionne     case PS_InRootName:
231c7d3c844SLouis Dionne     case PS_InRootDir:
232c7d3c844SLouis Dionne     case PS_InFilenames:
233c7d3c844SLouis Dionne       return &RawEntry.back() + 1;
234c7d3c844SLouis Dionne     case PS_InTrailingSep:
235c7d3c844SLouis Dionne     case PS_AtEnd:
236c7d3c844SLouis Dionne       return getAfterBack();
237c7d3c844SLouis Dionne     }
238c7d3c844SLouis Dionne     __libcpp_unreachable();
239c7d3c844SLouis Dionne   }
240c7d3c844SLouis Dionne 
241c7d3c844SLouis Dionne   /// \brief Return a pointer to the first character in the currently lexed
242c7d3c844SLouis Dionne   ///   element.
getCurrentTokenStartPosPathParser243c7d3c844SLouis Dionne   PosPtr getCurrentTokenStartPos() const noexcept {
244*731db06aSNikolas Klauser     switch (State_) {
245c7d3c844SLouis Dionne     case PS_BeforeBegin:
246c7d3c844SLouis Dionne     case PS_InRootName:
247c7d3c844SLouis Dionne       return &Path.front();
248c7d3c844SLouis Dionne     case PS_InRootDir:
249c7d3c844SLouis Dionne     case PS_InFilenames:
250c7d3c844SLouis Dionne     case PS_InTrailingSep:
251c7d3c844SLouis Dionne       return &RawEntry.front();
252c7d3c844SLouis Dionne     case PS_AtEnd:
253c7d3c844SLouis Dionne       return &Path.back() + 1;
254c7d3c844SLouis Dionne     }
255c7d3c844SLouis Dionne     __libcpp_unreachable();
256c7d3c844SLouis Dionne   }
257c7d3c844SLouis Dionne 
258c7d3c844SLouis Dionne   // Consume all consecutive separators.
consumeAllSeparatorsPathParser259c7d3c844SLouis Dionne   PosPtr consumeAllSeparators(PosPtr P, PosPtr End) const noexcept {
260c7d3c844SLouis Dionne     if (P == nullptr || P == End || !isSeparator(*P))
261c7d3c844SLouis Dionne       return nullptr;
262c7d3c844SLouis Dionne     const int Inc = P < End ? 1 : -1;
263c7d3c844SLouis Dionne     P += Inc;
264c7d3c844SLouis Dionne     while (P != End && isSeparator(*P))
265c7d3c844SLouis Dionne       P += Inc;
266c7d3c844SLouis Dionne     return P;
267c7d3c844SLouis Dionne   }
268c7d3c844SLouis Dionne 
269c7d3c844SLouis Dionne   // Consume exactly N separators, or return nullptr.
consumeNSeparatorsPathParser270c7d3c844SLouis Dionne   PosPtr consumeNSeparators(PosPtr P, PosPtr End, int N) const noexcept {
271c7d3c844SLouis Dionne     PosPtr Ret = consumeAllSeparators(P, End);
272c7d3c844SLouis Dionne     if (Ret == nullptr)
273c7d3c844SLouis Dionne       return nullptr;
274c7d3c844SLouis Dionne     if (P < End) {
275c7d3c844SLouis Dionne       if (Ret == P + N)
276c7d3c844SLouis Dionne         return Ret;
277c7d3c844SLouis Dionne     } else {
278c7d3c844SLouis Dionne       if (Ret == P - N)
279c7d3c844SLouis Dionne         return Ret;
280c7d3c844SLouis Dionne     }
281c7d3c844SLouis Dionne     return nullptr;
282c7d3c844SLouis Dionne   }
283c7d3c844SLouis Dionne 
consumeNamePathParser284c7d3c844SLouis Dionne   PosPtr consumeName(PosPtr P, PosPtr End) const noexcept {
285c7d3c844SLouis Dionne     PosPtr Start = P;
286c7d3c844SLouis Dionne     if (P == nullptr || P == End || isSeparator(*P))
287c7d3c844SLouis Dionne       return nullptr;
288c7d3c844SLouis Dionne     const int Inc = P < End ? 1 : -1;
289c7d3c844SLouis Dionne     P += Inc;
290c7d3c844SLouis Dionne     while (P != End && !isSeparator(*P))
291c7d3c844SLouis Dionne       P += Inc;
292c7d3c844SLouis Dionne     if (P == End && Inc < 0) {
293c7d3c844SLouis Dionne       // Iterating backwards and consumed all the rest of the input.
294c7d3c844SLouis Dionne       // Check if the start of the string would have been considered
295c7d3c844SLouis Dionne       // a root name.
296c7d3c844SLouis Dionne       PosPtr RootEnd = consumeRootName(End + 1, Start);
297c7d3c844SLouis Dionne       if (RootEnd)
298c7d3c844SLouis Dionne         return RootEnd - 1;
299c7d3c844SLouis Dionne     }
300c7d3c844SLouis Dionne     return P;
301c7d3c844SLouis Dionne   }
302c7d3c844SLouis Dionne 
consumeDriveLetterPathParser303c7d3c844SLouis Dionne   PosPtr consumeDriveLetter(PosPtr P, PosPtr End) const noexcept {
304c7d3c844SLouis Dionne     if (P == End)
305c7d3c844SLouis Dionne       return nullptr;
306c7d3c844SLouis Dionne     if (P < End) {
307c7d3c844SLouis Dionne       if (P + 1 == End || !isDriveLetter(P[0]) || P[1] != ':')
308c7d3c844SLouis Dionne         return nullptr;
309c7d3c844SLouis Dionne       return P + 2;
310c7d3c844SLouis Dionne     } else {
311c7d3c844SLouis Dionne       if (P - 1 == End || !isDriveLetter(P[-1]) || P[0] != ':')
312c7d3c844SLouis Dionne         return nullptr;
313c7d3c844SLouis Dionne       return P - 2;
314c7d3c844SLouis Dionne     }
315c7d3c844SLouis Dionne   }
316c7d3c844SLouis Dionne 
consumeNetworkRootPathParser317c7d3c844SLouis Dionne   PosPtr consumeNetworkRoot(PosPtr P, PosPtr End) const noexcept {
318c7d3c844SLouis Dionne     if (P == End)
319c7d3c844SLouis Dionne       return nullptr;
320c7d3c844SLouis Dionne     if (P < End)
321c7d3c844SLouis Dionne       return consumeName(consumeNSeparators(P, End, 2), End);
322c7d3c844SLouis Dionne     else
323c7d3c844SLouis Dionne       return consumeNSeparators(consumeName(P, End), End, 2);
324c7d3c844SLouis Dionne   }
325c7d3c844SLouis Dionne 
consumeRootNamePathParser326c7d3c844SLouis Dionne   PosPtr consumeRootName(PosPtr P, PosPtr End) const noexcept {
327c7d3c844SLouis Dionne #if defined(_LIBCPP_WIN32API)
328c7d3c844SLouis Dionne     if (PosPtr Ret = consumeDriveLetter(P, End))
329c7d3c844SLouis Dionne       return Ret;
330c7d3c844SLouis Dionne     if (PosPtr Ret = consumeNetworkRoot(P, End))
331c7d3c844SLouis Dionne       return Ret;
332c7d3c844SLouis Dionne #endif
333c7d3c844SLouis Dionne     return nullptr;
334c7d3c844SLouis Dionne   }
335c7d3c844SLouis Dionne };
336c7d3c844SLouis Dionne 
separate_filename(string_view_t const & s)33721853b96SLouis Dionne inline string_view_pair separate_filename(string_view_t const& s) {
338c7d3c844SLouis Dionne   if (s == PATHSTR(".") || s == PATHSTR("..") || s.empty())
339c7d3c844SLouis Dionne     return string_view_pair{s, PATHSTR("")};
340c7d3c844SLouis Dionne   auto pos = s.find_last_of('.');
341c7d3c844SLouis Dionne   if (pos == string_view_t::npos || pos == 0)
342c7d3c844SLouis Dionne     return string_view_pair{s, string_view_t{}};
343c7d3c844SLouis Dionne   return string_view_pair{s.substr(0, pos), s.substr(pos)};
344c7d3c844SLouis Dionne }
345c7d3c844SLouis Dionne 
createView(PosPtr S,PosPtr E)3469783f28cSLouis Dionne inline string_view_t createView(PosPtr S, PosPtr E) noexcept { return {S, static_cast<size_t>(E - S) + 1}; }
347c7d3c844SLouis Dionne 
348c7d3c844SLouis Dionne } // namespace parser
349c7d3c844SLouis Dionne 
350c7d3c844SLouis Dionne _LIBCPP_END_NAMESPACE_FILESYSTEM
351c7d3c844SLouis Dionne 
352c7d3c844SLouis Dionne #endif // PATH_PARSER_H
353