working on util::filesystem
authorJacob Lifshay <programmerjake@gmail.com>
Tue, 27 Jun 2017 00:22:27 +0000 (17:22 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Tue, 27 Jun 2017 08:07:11 +0000 (01:07 -0700)
src/util/CMakeLists.txt
src/util/filesystem.cpp [new file with mode: 0644]
src/util/filesystem.h
src/util/text.h
src/util/util_test.cpp [new file with mode: 0644]

index 3f0fea948eb4e922f6862fb137be5b7c76bf955b..43d6a8ae988dc8c738b51779b364e4ee88927e97 100644 (file)
@@ -22,6 +22,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
 set(sources bit_intrinsics.cpp
             bitset.cpp
             copy_cv_ref.cpp
+            filesystem.cpp
             in_place.cpp
             invoke.cpp
             is_referenceable.cpp
@@ -32,3 +33,4 @@ set(sources bit_intrinsics.cpp
             variant.cpp
             void_t.cpp)
 add_library(util STATIC ${sources})
+add_executable(util_test ${sources} util_test.cpp)
diff --git a/src/util/filesystem.cpp b/src/util/filesystem.cpp
new file mode 100644 (file)
index 0000000..b95952e
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2017 Jacob Lifshay
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+#include "filesystem.h"
+#include <cstdlib>
+#include <iostream>
+
+namespace vulkan_cpu
+{
+namespace util
+{
+namespace filesystem
+{
+namespace detail
+{
+#if 1
+#warning testing util::filesystem::path
+struct Path_tester
+{
+    template <typename Path, bool Show_parts = true>
+    static void write_path(const Path &path)
+    {
+        std::cout << path << ": kind=";
+        switch(path.kind)
+        {
+        case Path_part_kind::file_name:
+            std::cout << "file_name";
+            break;
+        case Path_part_kind::multiple_parts:
+            std::cout << "multiple_parts";
+            break;
+        case Path_part_kind::root_dir:
+            std::cout << "root_dir";
+            break;
+        case Path_part_kind::relative_root_name:
+            std::cout << "relative_root_name";
+            break;
+        case Path_part_kind::absolute_root_name:
+            std::cout << "absolute_root_name";
+            break;
+        case Path_part_kind::path_separator:
+            std::cout << "path_separator";
+            break;
+        }
+        if(Show_parts)
+        {
+            std::cout << " parts=[";
+            auto separator = "";
+            for(auto &part : path.parts)
+            {
+                std::cout << separator;
+                separator = ", ";
+                write_path<Path, false>(part);
+            }
+            std::cout << "]";
+        }
+    }
+    template <Path_traits_kind Traits_kind>
+    static void test_path(const char *traits_kind_name)
+    {
+#if 0
+        typedef basic_path<> Path;
+#else
+        typedef basic_path<Traits_kind> Path;
+#endif
+        std::cout << "testing basic_path<" << traits_kind_name << ">" << std::endl;
+        for(auto *test_path_string : {
+                "",
+                ".",
+                "..",
+                "C:",
+                "C:\\",
+                "C:/",
+                "/",
+                "//",
+                "//a",
+                "//a/",
+                "\\",
+                "\\\\",
+                "\\\\a",
+                "\\\\a\\",
+                "a/",
+                "a/.",
+                "a/..",
+                "a/...",
+                "a/a",
+                "a/.a",
+                "a/a.",
+                "a/a.a",
+                "a/.a.",
+                "a/.a.a",
+            })
+        {
+            Path p(test_path_string);
+            std::cout << "'" << test_path_string << "' -> ";
+            write_path(p);
+            std::cout << std::endl;
+            std::cout << "make_preferred -> " << Path(p).make_preferred() << std::endl;
+            std::cout << "remove_filename -> " << Path(p).remove_filename() << std::endl;
+            std::cout << "lexically_normal -> " << p.lexically_normal() << std::endl;
+            std::cout << "operator/=:";
+            for(auto *appended_path : {
+                    "",
+                    "/abc",
+                    "C:abc",
+                    "//a/abc",
+                    "C:/abc",
+                    "abc",
+                })
+            {
+                std::cout << " \"" << appended_path << "\"->"
+                          << Path(p).operator/=(appended_path);
+            }
+            std::cout << std::endl;
+        }
+    }
+    static void test()
+    {
+        test_path<Path_traits_kind::posix>("posix");
+        test_path<Path_traits_kind::windows>("windows");
+        std::string().assign("", 0);
+    }
+    Path_tester()
+    {
+        test();
+        std::exit(1);
+    }
+};
+
+namespace
+{
+Path_tester path_tester;
+}
+#endif
+}
+}
+}
+}
index 7ede99922c3b8225c851d135fd6d031d3fd5a68e..080182afcd46f167ff34924ef3001e240ba1b94a 100644 (file)
@@ -37,6 +37,9 @@
 #include "optional.h"
 #include "text.h"
 #include <cstdint>
+#include <iomanip>
+#include <istream>
+#include <ostream>
 
 namespace vulkan_cpu
 {
@@ -63,6 +66,7 @@ struct Path_traits
 {
     typedef char value_type;
     static constexpr value_type preferred_separator = '/';
+    static constexpr bool needs_root_name_to_be_absolute = false;
 };
 
 template <>
@@ -70,14 +74,17 @@ struct Path_traits<Path_traits_kind::windows>
 {
     typedef wchar_t value_type;
     static constexpr value_type preferred_separator = L'\\';
+    static constexpr bool needs_root_name_to_be_absolute = true;
 };
 
-enum class Path_kind
+enum class Path_part_kind
 {
-    root_name,
+    relative_root_name, // root name that has a current directory, like "C:" in windows
+    absolute_root_name, // root name that can't have a current directory, like "\\ABC" in windows
     root_dir,
     file_name,
     multiple_parts,
+    path_separator,
 };
 
 template <typename T>
@@ -165,10 +172,10 @@ public:
         if(base_iterator)
         {
             assert(!rt.base_iterator);
-            return *base_iterator == value_type();
+            return **base_iterator == value_type();
         }
         if(rt.base_iterator)
-            return *rt.base_iterator == value_type();
+            return **rt.base_iterator == value_type();
         return true;
     }
     bool operator!=(const Path_convert_single_iterator_adaptor &rt) const
@@ -178,7 +185,7 @@ public:
     bool operator==(Path_iterator_sentinel) const
     {
         if(base_iterator)
-            return *base_iterator == value_type();
+            return **base_iterator == value_type();
         return true;
     }
     bool operator!=(Path_iterator_sentinel) const
@@ -251,7 +258,8 @@ private:
             *this = Path_convert_iterator();
         else
         {
-            encode_result = text::Decode_encode_functions<Dest_char_type>::encode(ch);
+            encode_result =
+                text::Decode_encode_functions<Dest_char_type>::encode(ch, text::Convert_options());
             encode_result_index = 0;
         }
     }
@@ -281,11 +289,11 @@ public:
         operator++();
         return retval;
     }
-    const char32_t &operator*() const noexcept
+    const Dest_char_type &operator*() const noexcept
     {
         return encode_result[encode_result_index];
     }
-    const char32_t *operator->() const noexcept
+    const Dest_char_type *operator->() const noexcept
     {
         return &encode_result[encode_result_index];
     }
@@ -320,12 +328,15 @@ struct Path_convert_range<Path_char_type,
                               enable_if<Path_is_convertable_iterator_type<Iterator>::value>::type>
 {
     static constexpr bool is_convertible = true;
-    template <typename Sentinel>
-    static std::basic_string<Path_char_type> to_string(Iterator iterator, Sentinel sentinel)
+    template <typename Traits = std::char_traits<Path_char_type>,
+              typename Allocator = std::allocator<Path_char_type>,
+              typename Sentinel>
+    static std::basic_string<Path_char_type, Traits, Allocator> to_string(
+        Iterator iterator, Sentinel sentinel, const Allocator &a = Allocator())
     {
         typedef Path_convert_iterator<Path_char_type, Iterator, Sentinel> Convert_iterator;
-        return std::basic_string<Path_char_type>(Convert_iterator(iterator, sentinel),
-                                                 Convert_iterator());
+        return std::basic_string<Path_char_type, Traits, Allocator>(
+            Convert_iterator(iterator, sentinel), Convert_iterator(), a);
     }
 };
 
@@ -336,14 +347,20 @@ struct Path_convert_range<typename Path_is_convertable_iterator_type<Iterator>::
 {
     static constexpr bool is_convertible = true;
     typedef typename Path_is_convertable_iterator_type<Iterator>::Char_type Char_type;
-    static std::basic_string<Char_type> to_string(Iterator iterator, Iterator sentinel)
+    static std::basic_string<Char_type> to_string(
+        Iterator iterator,
+        Iterator sentinel,
+        const std::allocator<Char_type> &a = std::allocator<Char_type>())
     {
-        return std::basic_string<Char_type>(iterator, sentinel);
+        return std::basic_string<Char_type>(iterator, sentinel, a);
     }
-    template <typename Sentinel>
-    static std::basic_string<Char_type> to_string(Iterator iterator, Sentinel sentinel)
+    template <typename Traits = std::char_traits<Char_type>,
+              typename Allocator = std::allocator<Char_type>,
+              typename Sentinel>
+    static std::basic_string<Char_type, Traits, Allocator> to_string(
+        Iterator iterator, Sentinel sentinel, const Allocator &a = Allocator())
     {
-        std::basic_string<Char_type> retval;
+        std::basic_string<Char_type, Traits, Allocator> retval(a);
         while(iterator != sentinel)
             retval += *iterator++;
         return retval;
@@ -372,10 +389,14 @@ struct Path_convert_source<Path_char_type,
                                typename std::basic_string<Source_char_type, Traits, Allocator>::
                                    const_iterator> Convert_range;
     static constexpr bool is_convertible = true;
-    static std::basic_string<Path_char_type> to_string(
-        const std::basic_string<Source_char_type, Traits, Allocator> &source)
+    template <typename Dest_traits = std::char_traits<Path_char_type>,
+              typename Dest_allocator = std::allocator<Path_char_type>>
+    static std::basic_string<Path_char_type, Dest_traits, Dest_allocator> to_string(
+        const std::basic_string<Source_char_type, Traits, Allocator> &source,
+        const Allocator &a = Allocator())
     {
-        return Convert_range::to_string(source.begin(), source.end());
+        return Convert_range::template to_string<Dest_traits, Dest_allocator>(
+            source.begin(), source.end(), a);
     }
 };
 
@@ -393,24 +414,30 @@ struct
                                typename basic_string_view<Source_char_type, Traits>::const_iterator>
         Convert_range;
     static constexpr bool is_convertible = true;
-    static std::basic_string<Path_char_type> to_string(
-        const basic_string_view<Source_char_type, Traits> &source)
+    template <typename Dest_traits = std::char_traits<Path_char_type>,
+              typename Allocator = std::allocator<Path_char_type>>
+    static std::basic_string<Path_char_type, Dest_traits, Allocator> to_string(
+        const basic_string_view<Source_char_type, Traits> &source, const Allocator &a = Allocator())
     {
-        return Convert_range::to_string(source.begin(), source.end());
+        return Convert_range::template to_string<Dest_traits, Allocator>(
+            source.begin(), source.end(), a);
     }
 };
 
 template <typename Char_type>
-struct Path_convert_source<Char_type,
-                           std::basic_string<Char_type>,
-                           typename std::
-                               enable_if<Path_convert_range<Char_type,
-                                                            typename std::basic_string<Char_type>::
-                                                                const_iterator>::is_convertible>::
-                                   type>
+struct Path_convert_source<Char_type, std::basic_string<Char_type>, void>
 {
     static constexpr bool is_convertible = true;
-    static std::basic_string<Char_type> to_string(const std::basic_string<Char_type> &source)
+    template <typename Traits = std::char_traits<Char_type>,
+              typename Allocator = std::allocator<Char_type>>
+    static std::basic_string<Char_type, Traits, Allocator> to_string(
+        const std::basic_string<Char_type> &source, const Allocator &a = Allocator())
+    {
+        return std::basic_string<Char_type, Traits, Allocator>(source.begin(), source.end(), a);
+    }
+    static std::basic_string<Char_type> to_string(
+        const std::basic_string<Char_type> &source,
+        const std::allocator<Char_type> & = std::allocator<Char_type>())
     {
         return source;
     }
@@ -419,15 +446,20 @@ struct Path_convert_source<Char_type,
 template <typename Char_type, typename Iterator>
 struct Path_convert_source<Char_type,
                            Iterator,
-                           void_t<typename Path_is_convertable_iterator_type<Iterator>::Char_type>>
+                           typename std::enable_if<!std::is_same<
+                               typename Path_is_convertable_iterator_type<Iterator>::Char_type,
+                               Char_type>::value>::type>
 {
     static constexpr bool is_convertible = true;
     typedef Path_convert_range<Char_type, Path_convert_single_iterator_adaptor<Iterator>>
         Convert_range;
-    static std::basic_string<Char_type> to_string(Iterator iterator)
+    template <typename Traits = std::char_traits<Char_type>,
+              typename Allocator = std::allocator<Char_type>>
+    static std::basic_string<Char_type, Traits, Allocator> to_string(
+        Iterator iterator, const Allocator &a = Allocator())
     {
-        return Convert_range::to_string(Path_convert_single_iterator_adaptor<Iterator>(iterator),
-                                        Path_iterator_sentinel());
+        return Convert_range::template to_string<Traits, Allocator>(
+            Path_convert_single_iterator_adaptor<Iterator>(iterator), Path_iterator_sentinel(), a);
     }
 };
 
@@ -435,19 +467,68 @@ template <typename Char_type>
 struct Path_convert_source<Char_type, const Char_type *, void>
 {
     static constexpr bool is_convertible = true;
-    static std::basic_string<Char_type> to_string(const Char_type *source)
+    template <typename Traits = std::char_traits<Char_type>,
+              typename Allocator = std::allocator<Char_type>>
+    static std::basic_string<Char_type, Traits, Allocator> to_string(
+        const Char_type *source, const Allocator &a = Allocator())
+    {
+        return {source, a};
+    }
+};
+
+template <typename Path_char_type, typename Source_char_type, std::size_t N>
+struct Path_convert_source<Path_char_type, Source_char_type[N], void>
+    : public Path_convert_source<Path_char_type, const Source_char_type *, void>
+{
+};
+
+struct Path_index_range
+{
+    std::size_t begin;
+    std::size_t end;
+    constexpr Path_index_range() noexcept : begin(0), end(0)
+    {
+    }
+    constexpr Path_index_range(std::size_t begin, std::size_t end) noexcept : begin(begin), end(end)
+    {
+    }
+    template <typename Char_type, typename Traits>
+    constexpr Path_index_range(basic_string_view<Char_type, Traits> str,
+                               typename basic_string_view<Char_type, Traits>::iterator begin,
+                               typename basic_string_view<Char_type, Traits>::iterator end) noexcept
+        : begin(begin - str.begin()),
+          end(end - str.begin())
     {
-        return source;
+    }
+    constexpr bool empty() const noexcept
+    {
+        return begin == end;
+    }
+    constexpr std::size_t size() const noexcept
+    {
+        return end - begin;
     }
 };
 
-#error finish
+struct Path_tester;
+}
 
-template <Path_traits_kind Traits_kind = default_path_traits_kind,
-          typename Char_type = typename Path_traits<Traits_kind>::value_type,
-          Char_type Preferred_separator = Path_traits<Traits_kind>::preferred_separator>
+template <detail::Path_traits_kind Traits_kind = detail::default_path_traits_kind,
+          typename Char_type = typename detail::Path_traits<Traits_kind>::value_type,
+          Char_type Preferred_separator = detail::Path_traits<Traits_kind>::preferred_separator,
+          bool Needs_root_name_to_be_absolute =
+              detail::Path_traits<Traits_kind>::needs_root_name_to_be_absolute>
 class basic_path
 {
+    friend struct detail::Path_tester;
+    static_assert(std::is_same<Char_type, char>::value || std::is_same<Char_type, wchar_t>::value,
+                  "");
+
+private:
+    typedef detail::Path_traits_kind Path_traits_kind;
+    typedef detail::Path_part_kind Path_part_kind;
+    typedef detail::Path_index_range Path_index_range;
+
 public:
     typedef Char_type value_type;
     typedef std::basic_string<Char_type> string_type;
@@ -659,7 +740,72 @@ private:
 private:
     Parts parts;
     string_type value;
-    Path_kind kind;
+    detail::Path_part_kind kind;
+
+public:
+    class iterator
+    {
+        template <detail::Path_traits_kind, typename Char_type2, Char_type2, bool>
+        friend class basic_path;
+
+    public:
+        typedef basic_path value_type;
+        typedef const basic_path *pointer;
+        typedef const basic_path &reference;
+        typedef std::ptrdiff_t difference_type;
+        typedef std::bidirectional_iterator_tag iterator_category;
+
+    private:
+        const basic_path *path;
+        std::size_t index;
+        constexpr iterator(const basic_path *path, std::size_t index) noexcept : path(path),
+                                                                                 index(index)
+        {
+        }
+
+    public:
+        constexpr iterator() noexcept : path(nullptr), index()
+        {
+        }
+        constexpr iterator &operator++() noexcept
+        {
+            index++;
+            return *this;
+        }
+        constexpr iterator &operator--() noexcept
+        {
+            index--;
+            return *this;
+        }
+        constexpr iterator operator++(int) noexcept
+        {
+            return iterator(path, index++);
+        }
+        constexpr iterator operator--(int) noexcept
+        {
+            return iterator(path, index--);
+        }
+        const basic_path *operator->() const
+        {
+            assert(path);
+            if(path->kind == Path_part_kind::multiple_parts)
+                return &path->parts[index];
+            return path;
+        }
+        const basic_path &operator*() const
+        {
+            return *operator->();
+        }
+        constexpr bool operator==(const iterator &rt) const noexcept
+        {
+            return index == rt.index;
+        }
+        constexpr bool operator!=(const iterator &rt) const noexcept
+        {
+            return index != rt.index;
+        }
+    };
+    typedef iterator const_iterator;
 
 private:
     static constexpr bool is_ascii_letter(Char_type v) noexcept
@@ -673,15 +819,15 @@ private:
             return true;
         return false;
     }
-    static constexpr bool is_separator(Char_type v) noexcept
+    template <typename Char_type2>
+    static constexpr bool is_separator(Char_type2 v) noexcept
     {
-        return v == static_cast<Char_type>('/') || v == preferred_separator;
+        return v == static_cast<Char_type2>('/')
+               || v == static_cast<Char_type2>(preferred_separator);
     }
-    template <typename Fn>
-    static void parse(string_view_type value, Fn callback, format fmt = auto_format) noexcept(
-        noexcept(callback(typename string_view_type::iterator(),
-                          typename string_view_type::iterator(),
-                          Path_kind())))
+    template <bool Ignore_root_parts = false, typename Fn>
+    static bool parse(string_view_type value, Fn callback, format fmt = auto_format) noexcept(
+        noexcept(callback(Path_index_range(), Path_part_kind())))
     {
         constexpr Char_type colon = ':';
         typedef typename std::char_traits<Char_type>::int_type Int_type;
@@ -700,15 +846,17 @@ private:
             return std::char_traits<Char_type>::to_int_type(*char_iter++);
         };
         if(value.empty())
-            return;
-        if(Traits_kind == Path_traits_kind::windows && value.size() >= 2
+            return true;
+        if(!Ignore_root_parts && Traits_kind == Path_traits_kind::windows && value.size() >= 2
            && is_ascii_letter(value[0])
            && value[1] == colon)
         {
             char_iter += 2;
-            callback(value.begin(), char_iter, Path_kind::root_name);
+            if(!callback(Path_index_range(value, value.begin(), char_iter),
+                         Path_part_kind::relative_root_name))
+                return false;
         }
-        else if(Traits_kind == Path_traits_kind::windows && value.size() >= 2
+        else if(!Ignore_root_parts && Traits_kind == Path_traits_kind::windows && value.size() >= 2
                 && is_separator(value[0])
                 && is_separator(value[1]))
         {
@@ -716,16 +864,24 @@ private:
                 get();
             while(peek() != eof && !is_separator(peek()))
                 get();
-            callback(value.begin(), char_iter, Path_kind::root_name);
+            if(!callback(Path_index_range(value, value.begin(), char_iter),
+                         Path_part_kind::absolute_root_name))
+                return false;
         }
-        if(peek() != eof && is_separator(peek()))
+        if(!Ignore_root_parts && peek() != eof && is_separator(peek()))
         {
             auto start_iter = char_iter;
             do
             {
                 get();
             } while(peek() != eof && is_separator(peek()));
-            callback(start_iter, char_iter, Path_kind::root_dir);
+            if(!callback(Path_index_range(value, start_iter, char_iter), Path_part_kind::root_dir))
+                return false;
+        }
+        if(Ignore_root_parts && peek() != eof && is_separator(peek()))
+        {
+            if(!callback(Path_index_range(value, char_iter, char_iter), Path_part_kind::file_name))
+                return false;
         }
         if(peek() != eof && !is_separator(peek()))
         {
@@ -734,34 +890,55 @@ private:
             {
                 get();
             } while(peek() != eof && !is_separator(peek()));
-            callback(start_iter, char_iter, Path_kind::file_name);
+            if(!callback(Path_index_range(value, start_iter, char_iter), Path_part_kind::file_name))
+                return false;
         }
         while(peek() != eof)
         {
+            auto start_iter = char_iter;
             do
             {
                 get();
             } while(peek() != eof && is_separator(peek()));
-            auto start_iter = char_iter;
+            if(!callback(Path_index_range(value, start_iter, char_iter),
+                         Path_part_kind::path_separator))
+                return false;
+            start_iter = char_iter;
             while(peek() != eof && !is_separator(peek()))
                 get();
-            callback(start_iter, char_iter, Path_kind::file_name);
+            if(!callback(Path_index_range(value, start_iter, char_iter), Path_part_kind::file_name))
+                return false;
         }
+        return true;
     }
     void parse(format fmt = auto_format)
     {
-        auto last_part_kind = Path_kind::multiple_parts;
+        constexpr Char_type generic_separator = '/';
+        auto last_part_kind = Path_part_kind::multiple_parts;
         std::size_t part_count = 0;
+        bool need_generic_conversion = false;
         parse(value,
-              [&]([[gnu::unused]] typename string_view_type::iterator part_string_begin,
-                  [[gnu::unused]] typename string_view_type::iterator part_string_end,
-                  Path_kind part_kind) noexcept
+              [&](Path_index_range index_range, Path_part_kind part_kind) noexcept
               {
+                  if(part_kind == Path_part_kind::path_separator)
+                      return true;
+                  if(generic_separator != preferred_separator && !need_generic_conversion)
+                  {
+                      for(std::size_t i = index_range.begin; i < index_range.end; i++)
+                      {
+                          if(is_separator(value[i]) && value[i] != generic_separator)
+                          {
+                              need_generic_conversion = true;
+                              break;
+                          }
+                      }
+                  }
                   last_part_kind = part_kind;
                   part_count++;
+                  return true;
               },
               fmt);
-        if(part_count == 1)
+        if(part_count == 1 && !need_generic_conversion)
         {
             kind = last_part_kind;
             parts.clear();
@@ -769,67 +946,189 @@ private:
         }
         else
         {
-            kind = Path_kind::multiple_parts;
+            kind = Path_part_kind::multiple_parts;
         }
         while(parts.size() > part_count)
             parts.pop_back();
         parts.reserve(part_count);
         std::size_t part_index = 0;
         parse(value,
-              [&](typename string_view_type::iterator part_string_begin,
-                  typename string_view_type::iterator part_string_end,
-                  Path_kind part_kind)
+              [&](Path_index_range index_range, Path_part_kind part_kind)
               {
+                  if(part_kind == Path_part_kind::path_separator)
+                      return true;
                   if(part_index >= parts.size())
                       parts.emplace_back();
-                  // copy manually because libstdc++ allocates a new string in basic_string.assign
-                  parts[part_index].value.clear();
-                  parts[part_index].value.reserve(part_string_end - part_string_begin);
-                  for(auto i = part_string_begin; i != part_string_end; ++i)
-                      parts[part_index].value.push_back(*i);
+                  parts[part_index].value.assign(value.data() + index_range.begin,
+                                                 index_range.size());
                   parts[part_index].kind = part_kind;
+                  change_separator(parts[part_index].value, generic_separator);
                   part_index++;
+                  return true;
               },
               fmt);
     }
-    std::size_t get_filename_start_index() noexcept
+    static Path_index_range get_filename_index_range(string_view_type value) noexcept
     {
-        std::size_t retval = value.size();
-        auto value_view = string_view_type(value);
+        Path_index_range retval(value.size(), value.size());
         parse(value,
-              [&](typename string_view_type::iterator part_string_begin,
-                  [[gnu::unused]] typename string_view_type::iterator part_string_end,
-                  Path_kind part_kind) noexcept
+              [&](Path_index_range index_range, Path_part_kind part_kind) noexcept
               {
-                  if(part_kind == Path_kind::file_name)
-                      retval = part_string_begin - value_view.begin();
+                  if(part_kind == Path_part_kind::file_name)
+                      retval = index_range;
+                  return true;
               });
         return retval;
     }
-    std::size_t get_extension_start_index() noexcept
+    static Path_index_range get_stem_index_range(string_view_type value) noexcept
+    {
+        return get_stem_index_range(value, get_filename_index_range(value));
+    }
+    static Path_index_range get_stem_index_range(string_view_type value,
+                                                 Path_index_range filename_index_range) noexcept
     {
         constexpr Char_type dot = '.';
-        std::size_t index = get_filename_start_index();
-        if(index >= value.size())
-            return value.size();
-        if(value[index] == dot)
-            index++;
-        std::size_t retval = value.size();
-        while(index < value.size())
+        if(filename_index_range.size() <= 1)
+            return filename_index_range;
+        for(std::size_t i = filename_index_range.end; i > filename_index_range.begin; i--)
         {
-            if(value[index] == dot)
-                retval = index;
-            index++;
+            if(value[i - 1] == dot)
+            {
+                if(i == filename_index_range.begin + 1)
+                    return filename_index_range;
+                if(i == filename_index_range.begin + 2 && value[filename_index_range.begin] == dot)
+                    return filename_index_range;
+                return Path_index_range(filename_index_range.begin, i - 1);
+            }
         }
+        return filename_index_range;
+    }
+    static Path_index_range get_extension_index_range(string_view_type value) noexcept
+    {
+        return get_extension_index_range(value, get_filename_index_range(value));
+    }
+    static Path_index_range get_extension_index_range(
+        string_view_type value, Path_index_range filename_index_range) noexcept
+    {
+        return get_extension_index_range(
+            value, filename_index_range, get_stem_index_range(value, filename_index_range));
+    }
+    static Path_index_range get_extension_index_range([[gnu::unused]] string_view_type value,
+                                                      Path_index_range filename_index_range,
+                                                      Path_index_range stem_index_range) noexcept
+    {
+        return Path_index_range(stem_index_range.end, filename_index_range.end);
+    }
+    static Path_index_range get_root_name_index_range(string_view_type value) noexcept
+    {
+        Path_index_range retval(0, 0);
+        parse(value,
+              [&](Path_index_range index_range, Path_part_kind part_kind) noexcept
+              {
+                  if(part_kind == Path_part_kind::absolute_root_name
+                     || part_kind == Path_part_kind::relative_root_name)
+                      retval = index_range;
+                  return false;
+              });
+        return retval;
+    }
+    static Path_index_range get_root_dir_index_range(string_view_type value) noexcept
+    {
+        Path_index_range retval(0, 0);
+        parse(value,
+              [&](Path_index_range index_range, Path_part_kind part_kind) noexcept
+              {
+                  if(part_kind == Path_part_kind::root_dir)
+                  {
+                      retval = index_range;
+                  }
+                  else if(part_kind == Path_part_kind::absolute_root_name
+                          || part_kind == Path_part_kind::relative_root_name)
+                  {
+                      retval = Path_index_range(index_range.end, index_range.end);
+                      return true;
+                  }
+                  return false;
+              });
+        return retval;
+    }
+    static Path_index_range get_root_path_index_range(string_view_type value) noexcept
+    {
+        Path_index_range retval(0, 0);
+        parse(value,
+              [&](Path_index_range index_range, Path_part_kind part_kind) noexcept
+              {
+                  if(part_kind == Path_part_kind::absolute_root_name
+                     || part_kind == Path_part_kind::relative_root_name)
+                  {
+                      retval = index_range;
+                      return true;
+                  }
+                  else if(part_kind == Path_part_kind::root_dir)
+                  {
+                      retval.end = index_range.end;
+                      return false;
+                  }
+                  return false;
+              });
+        return retval;
+    }
+    static Path_index_range get_relative_path_index_range(string_view_type value) noexcept
+    {
+        Path_index_range retval(value.size(), value.size());
+        parse(value,
+              [&](Path_index_range index_range, Path_part_kind part_kind) noexcept
+              {
+                  if(part_kind == Path_part_kind::absolute_root_name
+                     || part_kind == Path_part_kind::relative_root_name
+                     || part_kind == Path_part_kind::root_dir)
+                  {
+                      return true;
+                  }
+                  retval.begin = index_range.begin;
+                  return false;
+              });
+        return retval;
+    }
+    static Path_index_range get_parent_path_index_range(string_view_type value) noexcept
+    {
+        Path_index_range retval(0, 0);
+        std::size_t last_file_name_end_index = 0;
+        parse(value,
+              [&](Path_index_range index_range, Path_part_kind part_kind) noexcept
+              {
+                  switch(part_kind)
+                  {
+                  case Path_part_kind::path_separator:
+                      return true;
+                  case Path_part_kind::absolute_root_name:
+                  case Path_part_kind::relative_root_name:
+                  case Path_part_kind::root_dir:
+                      retval.end = index_range.end;
+                      return true;
+                  case Path_part_kind::file_name:
+                      if(last_file_name_end_index != 0)
+                          retval.end = last_file_name_end_index;
+                      last_file_name_end_index = index_range.end;
+                      return true;
+                  case Path_part_kind::multiple_parts:
+                      break;
+                  }
+                  assert(false);
+                  return false;
+              });
         return retval;
     }
 
 public:
-    basic_path() noexcept : parts(), value(), kind(Path_kind::multiple_parts)
+    basic_path() noexcept : parts(), value(), kind(Path_part_kind::multiple_parts)
     {
     }
     basic_path(const basic_path &) = default;
-    basic_path(basic_path &&) noexcept = default;
+    basic_path(basic_path &&rt) noexcept : parts(), value(), kind()
+    {
+        swap(rt);
+    }
     basic_path(string_type &&source, format fmt = auto_format)
         : parts(), value(std::move(source)), kind()
     {
@@ -837,16 +1136,21 @@ public:
     }
     template <typename Source>
     basic_path(const Source &source, format fmt = auto_format)
-        : basic_path(Path_convert_source<Char_type, Source>::to_string(source), fmt)
+        : basic_path(detail::Path_convert_source<Char_type, Source>::to_string(source), fmt)
     {
     }
     template <typename Input_iterator>
     basic_path(Input_iterator first, Input_iterator last, format fmt = auto_format)
-        : basic_path(Path_convert_range<Char_type, Input_iterator>::to_string(first, last), fmt)
+        : basic_path(detail::Path_convert_range<Char_type, Input_iterator>::to_string(first, last),
+                     fmt)
     {
     }
     basic_path &operator=(const basic_path &rt) = default;
-    basic_path &operator=(basic_path &&rt) noexcept = default;
+    basic_path &operator=(basic_path &&rt) noexcept
+    {
+        basic_path(std::move(rt)).swap(*this);
+        return *this;
+    }
     basic_path &operator=(string_type &&new_value)
     {
         value = std::move(new_value);
@@ -863,16 +1167,150 @@ public:
     {
         return operator=(new_value);
     }
+    basic_path &assign(const string_type &new_value)
+    {
+        value = new_value;
+        parse();
+        return *this;
+    }
+    basic_path &assign(const string_view_type &new_value)
+    {
+        // use assign to prevent allocating a temporary string_type
+        value.assign(new_value.data(), new_value.size());
+        parse();
+        return *this;
+    }
     template <typename Source>
     basic_path &assign(const Source &source)
     {
-        assign(Path_convert_source<Char_type, Source>::to_string(source));
+        assign(detail::Path_convert_source<Char_type, Source>::to_string(source));
         return *this;
     }
     template <typename Input_iterator>
     basic_path &assign(Input_iterator first, Input_iterator last)
     {
-        assign(Path_convert_range<Char_type, Input_iterator>::to_string(first, last));
+        assign(detail::Path_convert_range<Char_type, Input_iterator>::to_string(first, last));
+        return *this;
+    }
+
+private:
+    template <bool Check_for_shared_memory = true>
+    void append_string(string_view_type str)
+    {
+        bool just_need_to_assign = is_absolute(str);
+        if(!just_need_to_assign && !get_root_name_index_range(str).empty())
+        {
+            auto my_root_name_index_range = get_root_name_index_range(value);
+            auto str_root_name_index_range = get_root_name_index_range(str);
+            if(my_root_name_index_range.empty()
+               || string_view_type(value)
+                          .substr(my_root_name_index_range.begin, my_root_name_index_range.size())
+                      == str.substr(str_root_name_index_range.begin,
+                                    str_root_name_index_range.size()))
+                just_need_to_assign = true;
+        }
+        if(just_need_to_assign)
+        {
+            assign(str);
+            return;
+        }
+        static_assert(std::is_same<typename string_view_type::iterator, const Char_type *>::value,
+                      "");
+        if(Check_for_shared_memory && str.begin() <= value.data() + value.size()
+           && str.end() >= value.data())
+        {
+            // value and str share memory, reallocate str
+            append_string<false>(static_cast<string_type>(str));
+            return;
+        }
+        auto str_root_name_index_range = get_root_name_index_range(str);
+        assert(str_root_name_index_range.begin == 0);
+        str.remove_prefix(str_root_name_index_range.end);
+        if(!get_root_dir_index_range(str).empty())
+        {
+            auto my_root_name_index_range = get_root_name_index_range(value);
+            assert(my_root_name_index_range.begin == 0);
+            value.resize(my_root_name_index_range.end);
+        }
+        else if(!get_filename_index_range(value).empty()
+                || (get_root_dir_index_range(value).empty() && is_absolute()))
+        {
+            value.reserve(value.size() + 1 + str.size());
+            value += preferred_separator;
+        }
+        value += str;
+        parse();
+    }
+
+public:
+    basic_path &operator/=(const basic_path &p)
+    {
+        append_string(p.value);
+        return *this;
+    }
+    basic_path &operator/=(const string_type &p)
+    {
+        append_string(p);
+    }
+    basic_path &operator/=(const string_view_type &p)
+    {
+        append_string(p);
+    }
+    template <typename Source>
+    basic_path &operator/=(const Source &source)
+    {
+        append_string(detail::Path_convert_source<Char_type, Source>::to_string(source));
+        return *this;
+    }
+    template <typename Source>
+    basic_path &append(const Source &source)
+    {
+        operator/=(source);
+        return *this;
+    }
+    template <typename Input_iterator>
+    basic_path &append(Input_iterator first, Input_iterator last)
+    {
+        append_string(
+            detail::Path_convert_range<Char_type, Input_iterator>::to_string(first, last));
+        return *this;
+    }
+    basic_path &operator+=(const basic_path &p)
+    {
+        value += p.value;
+        parse();
+        return *this;
+    }
+    basic_path &operator+=(const string_type &p)
+    {
+        value += p;
+        parse();
+        return *this;
+    }
+    basic_path &operator+=(const string_view_type &p)
+    {
+        value += p;
+        parse();
+        return *this;
+    }
+    template <typename Source>
+    basic_path &operator+=(const Source &source)
+    {
+        value += detail::Path_convert_source<Char_type, Source>::to_string(source);
+        parse();
+        return *this;
+    }
+    template <typename Source>
+    basic_path &concat(const Source &source)
+    {
+        operator+=(source);
+        return *this;
+    }
+    template <typename Input_iterator>
+    basic_path &concat(Input_iterator first, Input_iterator last)
+    {
+        value += detail::Path_convert_range<Char_type, Input_iterator>::to_string(first, last);
+        parse();
         return *this;
     }
     const Char_type *c_str() const noexcept
@@ -892,25 +1330,38 @@ public:
         value.clear();
         parse();
     }
-    basic_path &make_preferred()
+
+private:
+    template <typename Char_type2, typename Traits, typename Allocator>
+    static void change_separator(std::basic_string<Char_type2, Traits, Allocator> &str,
+                                 Char_type2 separator) noexcept
     {
-        for(auto &ch : value)
+        for(auto &ch : str)
         {
             if(is_separator(ch))
-                ch = preferred_separator;
+                ch = separator;
         }
+    }
+    basic_path &change_separator(Char_type separator) noexcept
+    {
+        change_separator(value, separator);
         for(auto &part : parts)
-        {
-            part.make_preferred();
-        }
+            change_separator(part.value, separator);
+        return *this;
+    }
+
+public:
+    basic_path &make_preferred() noexcept
+    {
+        change_separator(preferred_separator);
         return *this;
     }
     basic_path &remove_filename()
     {
-        std::size_t filename_start_index = get_filename_start_index();
-        if(filename_start_index < value.size())
+        auto filename_index_range = get_filename_index_range(value);
+        if(!filename_index_range.empty())
         {
-            value.resize(filename_start_index);
+            value.erase(filename_index_range.begin, filename_index_range.size());
             parse();
         }
         return *this;
@@ -924,9 +1375,9 @@ public:
     basic_path &replace_extension(const basic_path &replacement = basic_path())
     {
         constexpr Char_type dot = '.';
-        std::size_t extension_start_index = get_extension_start_index();
-        if(extension_start_index < value.size())
-            value.resize(extension_start_index);
+        auto extension_index_range = get_extension_index_range(value);
+        if(!extension_index_range.empty())
+            value.erase(extension_index_range.begin, extension_index_range.size());
         else if(replacement.value.empty())
             return *this;
         if(!replacement.value.empty() && replacement.value.front() != dot)
@@ -949,29 +1400,543 @@ public:
         parts.swap(other.parts);
         swap(kind, other.kind);
     }
-#error finish
+    bool has_root_path() const noexcept
+    {
+        return !get_root_path_index_range(value).empty();
+    }
+    bool has_root_name() const noexcept
+    {
+        return !get_root_name_index_range(value).empty();
+    }
+    bool has_root_directory() const noexcept
+    {
+        return !get_root_dir_index_range(value).empty();
+    }
+    bool has_relative_path() const noexcept
+    {
+        return !get_relative_path_index_range(value).empty();
+    }
+    bool has_parent_path() const noexcept
+    {
+        return !get_parent_path_index_range(value).empty();
+    }
+    bool has_filename() const noexcept
+    {
+        return !get_filename_index_range(value).empty();
+    }
+    bool has_stem() const noexcept
+    {
+        return !get_stem_index_range(value).empty();
+    }
+    bool has_extension() const noexcept
+    {
+        return !get_extension_index_range(value).empty();
+    }
+
+private:
+    static bool is_absolute(string_view_type value) noexcept
+    {
+        bool has_root_dir = false;
+        bool has_relative_root_name = false;
+        bool has_absolute_root_name = false;
+        parse(value,
+              [&]([[gnu::unused]] Path_index_range index_range, Path_part_kind part_kind) noexcept
+              {
+                  if(part_kind == Path_part_kind::relative_root_name)
+                  {
+                      has_relative_root_name = true;
+                      return true;
+                  }
+                  else if(part_kind == Path_part_kind::absolute_root_name)
+                  {
+                      has_absolute_root_name = true;
+                      return false;
+                  }
+                  else if(part_kind == Path_part_kind::root_dir)
+                  {
+                      has_root_dir = true;
+                  }
+                  return false;
+              });
+        if(has_absolute_root_name)
+            return true;
+        if(has_root_dir)
+        {
+            if(Needs_root_name_to_be_absolute)
+                return has_relative_root_name;
+            return true;
+        }
+        return false;
+    }
+
+public:
+    bool is_absolute() const noexcept
+    {
+        return is_absolute(value);
+    }
+    bool is_relative() const noexcept
+    {
+        return !is_absolute(value);
+    }
+    template <typename String_char_type,
+              typename String_traits_type = std::char_traits<String_char_type>,
+              typename Allocator = std::allocator<String_char_type>>
+    std::basic_string<String_char_type, String_traits_type, Allocator> string(
+        const Allocator &a = Allocator()) const
+    {
+        return detail::Path_convert_source<String_char_type,
+                                           string_type>::template to_string<String_traits_type,
+                                                                            Allocator>(value, a);
+    }
+    std::string string() const
+    {
+        return string<char>();
+    }
+    std::wstring wstring() const
+    {
+        return string<wchar_t>();
+    }
+    std::string u8string() const
+    {
+        return string<char>();
+    }
+    std::u16string u16string() const
+    {
+        return string<char16_t>();
+    }
+    std::u32string u32string() const
+    {
+        return string<char32_t>();
+    }
+    template <typename String_char_type,
+              typename String_traits_type = std::char_traits<String_char_type>,
+              typename Allocator = std::allocator<String_char_type>>
+    std::basic_string<String_char_type, String_traits_type, Allocator> generic_string(
+        const Allocator &a = Allocator()) const
+    {
+        auto retval =
+            detail::Path_convert_source<String_char_type,
+                                        string_type>::template to_string<String_traits_type,
+                                                                         Allocator>(value, a);
+        change_separator(retval, static_cast<String_char_type>('/'));
+        return retval;
+    }
+    std::string generic_string() const
+    {
+        return generic_string<char>();
+    }
+    std::wstring generic_wstring() const
+    {
+        return generic_string<wchar_t>();
+    }
+    std::string generic_u8string() const
+    {
+        return generic_string<char>();
+    }
+    std::u16string generic_u16string() const
+    {
+        return generic_string<char16_t>();
+    }
+    std::u32string generic_u32string() const
+    {
+        return generic_string<char32_t>();
+    }
+    template <typename Stream_char_type, typename Stream_traits_type>
+    friend std::basic_ostream<Stream_char_type, Stream_traits_type> &operator<<(
+        std::basic_ostream<Stream_char_type, Stream_traits_type> &os, const basic_path &p)
+    {
+        os << std::quoted(p.string<Stream_char_type, Stream_traits_type>());
+        return os;
+    }
+    template <typename Stream_char_type, typename Stream_traits_type>
+    friend std::basic_istream<Stream_char_type, Stream_traits_type> &operator>>(
+        std::basic_istream<Stream_char_type, Stream_traits_type> &is, basic_path &p)
+    {
+        std::basic_string<Stream_char_type, Stream_traits_type> str;
+        is >> std::quoted(str);
+        p = std::move(str);
+        return is;
+    }
+
+private:
+    static int compare_part(string_view_type a,
+                            Path_part_kind a_kind,
+                            string_view_type b,
+                            Path_part_kind b_kind) noexcept
+    {
+        constexpr Char_type generic_separator_char = '/';
+        constexpr string_view_type generic_separator(&generic_separator_char, 1);
+        if(a_kind == Path_part_kind::root_dir)
+            a = generic_separator;
+        if(b_kind == Path_part_kind::root_dir)
+            b = generic_separator;
+        for(std::size_t i = 0; i < a.size() && i < b.size(); i++)
+        {
+            Char_type a_char = a[i];
+            Char_type b_char = b[i];
+            if(a_char == preferred_separator)
+                a_char = generic_separator_char;
+            if(b_char == preferred_separator)
+                b_char = generic_separator_char;
+            if(a_char < b_char)
+                return -1;
+            if(a_char > b_char)
+                return 1;
+        }
+        if(a.size() < b.size())
+            return -1;
+        if(a.size() > b.size())
+            return 1;
+        return 0;
+    }
+
+public:
+    int compare(string_view_type str) const noexcept
+    {
+        int retval;
+        if(kind != Path_part_kind::multiple_parts)
+        {
+            retval = 1; // non-empty is more than empty
+            parse(str,
+                  [&](Path_index_range index_range, Path_part_kind part_kind) noexcept
+                  {
+                      if(part_kind == Path_part_kind::path_separator)
+                          return true;
+                      if(retval == 1) // initial value
+                          retval = compare_part(value,
+                                                kind,
+                                                str.substr(index_range.begin, index_range.size()),
+                                                part_kind);
+                      else
+                          retval = -1; // one-element is less than two-elements
+                      return retval == 0;
+                  });
+        }
+        else
+        {
+            retval = 0;
+            auto part_iter = parts.begin();
+            parse(str,
+                  [&](Path_index_range index_range, Path_part_kind part_kind) noexcept
+                  {
+                      if(part_kind == Path_part_kind::path_separator)
+                          return true;
+                      if(part_iter == parts.end())
+                      {
+                          retval = -1; // empty is less than non-empty
+                      }
+                      else
+                      {
+                          retval = compare_part(part_iter->value,
+                                                part_iter->kind,
+                                                str.substr(index_range.begin, index_range.size()),
+                                                part_kind);
+                          ++part_iter;
+                      }
+                      return retval == 0;
+                  });
+            if(retval == 0 && part_iter != parts.end())
+                retval = 1; // more-elements is more than fewer-elements
+        }
+        return retval;
+    }
+    int compare(const string_type &str) const noexcept
+    {
+        return compare(string_view_type(str));
+    }
+    int compare(const Char_type *str) const
+    {
+        return compare(string_view_type(str));
+    }
+    int compare(const basic_path &rt) const noexcept
+    {
+        return compare(rt.value);
+    }
+    iterator begin() const noexcept
+    {
+        return iterator(this, 0);
+    }
+    iterator end() const noexcept
+    {
+        return iterator(this, kind == Path_part_kind::multiple_parts ? parts.size() : 1);
+    }
+    basic_path root_name() const
+    {
+        auto iter = begin();
+        if(iter == end())
+            return {};
+        if(iter->kind == Path_part_kind::relative_root_name
+           || iter->kind == Path_part_kind::absolute_root_name)
+            return *iter;
+        return {};
+    }
+    basic_path root_directory() const
+    {
+        auto iter = begin();
+        if(iter == end())
+            return {};
+        if(iter->kind == Path_part_kind::relative_root_name
+           || iter->kind == Path_part_kind::absolute_root_name)
+            ++iter;
+        if(iter == end())
+            return {};
+        if(iter->kind == Path_part_kind::root_dir)
+            return *iter;
+        return {};
+    }
+    basic_path root_path() const
+    {
+        auto index_range = get_root_path_index_range(value);
+        if(index_range.empty())
+            return {};
+        return value.substr(index_range.begin, index_range.size());
+    }
+    basic_path relative_path() const
+    {
+        auto index_range = get_relative_path_index_range(value);
+        if(index_range.empty())
+            return {};
+        return value.substr(index_range.begin, index_range.size());
+    }
+    basic_path parent_path() const
+    {
+        auto index_range = get_parent_path_index_range(value);
+        if(index_range.empty())
+            return {};
+        return value.substr(index_range.begin, index_range.size());
+    }
+    basic_path filename() const
+    {
+        auto iter = end();
+        if(iter == begin())
+            return {};
+        --iter;
+        if(iter->kind == Path_part_kind::file_name)
+            return *iter;
+        return {};
+    }
+    basic_path stem() const
+    {
+        auto index_range = get_stem_index_range(value);
+        if(index_range.empty())
+            return {};
+        return value.substr(index_range.begin, index_range.size());
+    }
+    basic_path extension() const
+    {
+        auto index_range = get_extension_index_range(value);
+        if(index_range.empty())
+            return {};
+        return value.substr(index_range.begin, index_range.size());
+    }
+    bool empty() const noexcept
+    {
+        return begin() == end();
+    }
+    basic_path lexically_normal() const
+    {
+        constexpr Char_type dot = '.';
+        constexpr std::size_t dot_dot_size = 2;
+        constexpr Char_type dot_dot_storage[dot_dot_size + 1] = {dot, dot};
+        string_view_type dot_dot(dot_dot_storage, dot_dot_size);
+        if(empty())
+            return {};
+        auto relative_path_index_range = get_relative_path_index_range(value);
+        auto root_name_index_range = get_root_name_index_range(value);
+        bool has_root_dir = has_root_directory();
+        basic_path retval;
+        retval.value.reserve(value.size());
+        retval.value.assign(value.data() + relative_path_index_range.begin,
+                            relative_path_index_range.size());
+        std::size_t new_size = 0;
+        for(std::size_t i = 0; i < retval.value.size(); i++)
+        {
+            if(is_separator(retval.value[i]))
+            {
+                while(i + 1 < retval.value.size() && is_separator(retval.value[i + 1]))
+                    i++;
+                retval.value[new_size++] = preferred_separator;
+            }
+            else
+            {
+                retval.value[new_size++] = retval.value[i];
+            }
+        }
+        retval.value.resize(new_size);
+        new_size = 0;
+        bool last_was_separator = true;
+        for(std::size_t i = 0; i < retval.value.size(); i++)
+        {
+            if(last_was_separator && retval.value[i] == dot)
+            {
+                if(i + 1 >= retval.value.size())
+                    break; // don't write the dot
+                if(retval.value[i + 1] == preferred_separator)
+                {
+                    i++;
+                    last_was_separator = true;
+                    continue; // skip the dot and separator
+                }
+            }
+            if(retval.value[i] == preferred_separator)
+                last_was_separator = true;
+            else
+                last_was_separator = false;
+            retval.value[new_size++] = retval.value[i];
+        }
+        retval.value.resize(new_size);
+        retval.parts.reserve(parts.size());
+        new_size = 0;
+        parse<true>(retval.value,
+                    [&](Path_index_range index_range, Path_part_kind part_kind) noexcept
+                    {
+                        if(part_kind == Path_part_kind::path_separator)
+                            return true;
+                        assert(part_kind == Path_part_kind::file_name);
+                        if(index_range.size() == 2 && retval.value[index_range.begin] == dot
+                           && retval.value[index_range.begin + 1] == dot)
+                        {
+                            if(new_size == 0 && has_root_dir)
+                                return true;
+                            if(new_size != 0)
+                            {
+                                new_size--;
+                                return true;
+                            }
+                        }
+                        if(new_size >= retval.parts.size())
+                            retval.parts.emplace_back();
+                        retval.parts[new_size].value.assign(retval.value.data() + index_range.begin,
+                                                            index_range.size());
+                        retval.parts[new_size].kind = Path_part_kind::file_name;
+                        new_size++;
+                        return true;
+                    });
+        if(new_size >= 2 && retval.parts[new_size - 1].value.empty()
+           && retval.parts[new_size - 2].value == dot_dot)
+            new_size--;
+        std::size_t needed_space = 0;
+        if(!root_name_index_range.empty())
+            needed_space++;
+        if(has_root_dir)
+            needed_space++;
+        if(needed_space > 0)
+        {
+            while(retval.parts.size() < new_size + needed_space)
+                retval.parts.emplace_back();
+            for(std::size_t source = new_size - 1, target = new_size + needed_space - 1, i = 0;
+                i < new_size;
+                source--, target--, i++)
+                retval.parts[target] = std::move(retval.parts[source]);
+            std::size_t root_part_index = 0;
+            if(!root_name_index_range.empty())
+            {
+                retval.parts[root_part_index].value.assign(
+                    value.data() + root_name_index_range.begin, root_name_index_range.size());
+                change_separator(retval.parts[root_part_index].value, static_cast<Char_type>('/'));
+                retval.parts[root_part_index].parts = Parts();
+                retval.parts[root_part_index].kind = begin()->kind;
+                root_part_index++;
+            }
+            if(has_root_dir)
+            {
+                retval.parts[root_part_index].value.assign(1, static_cast<Char_type>('/'));
+                retval.parts[root_part_index].parts = Parts();
+                retval.parts[root_part_index].kind = Path_part_kind::root_dir;
+            }
+        }
+        if(new_size + needed_space == 0)
+        {
+            if(retval.parts.empty())
+                retval.parts.emplace_back();
+            retval.parts[new_size].value.assign(1, dot);
+            retval.parts[new_size].parts = Parts();
+            retval.parts[new_size].kind = Path_part_kind::file_name;
+            new_size++;
+        }
+        while(retval.parts.size() > new_size + needed_space)
+            retval.parts.pop_back();
+        retval.value.clear();
+        bool need_seperator = false;
+        for(auto &part : retval.parts)
+        {
+            switch(part.kind)
+            {
+            case Path_part_kind::absolute_root_name:
+            case Path_part_kind::relative_root_name:
+                retval.value += part.value;
+                change_separator(retval.value, preferred_separator);
+                need_seperator = false;
+                // absolute_root_name will be followed by root_dir if we need a seperator
+                continue;
+            case Path_part_kind::file_name:
+                if(need_seperator)
+                    retval.value += preferred_separator;
+                retval.value += part.value;
+                need_seperator = true;
+                continue;
+            case Path_part_kind::root_dir:
+                retval.value += preferred_separator;
+                need_seperator = false;
+                continue;
+            case Path_part_kind::path_separator:
+            case Path_part_kind::multiple_parts:
+                break;
+            }
+            assert(false);
+        }
+        retval.parse();
+        return retval;
+    }
+#warning finish
 };
 
-template <Path_traits_kind Traits_kind, typename Char_type, Char_type Preferred_separator>
-constexpr Char_type basic_path<Traits_kind, Char_type, Preferred_separator>::preferred_separator;
+template <detail::Path_traits_kind Traits_kind,
+          typename Char_type,
+          Char_type Preferred_separator,
+          bool Needs_root_name_to_be_absolute>
+void swap(
+    basic_path<Traits_kind, Char_type, Preferred_separator, Needs_root_name_to_be_absolute> &l,
+    basic_path<Traits_kind, Char_type, Preferred_separator, Needs_root_name_to_be_absolute>
+        &r) noexcept
+{
+    l.swap(r);
+}
+
+template <detail::Path_traits_kind Traits_kind,
+          typename Char_type,
+          Char_type Preferred_separator,
+          bool Needs_root_name_to_be_absolute>
+constexpr Char_type basic_path<Traits_kind,
+                               Char_type,
+                               Preferred_separator,
+                               Needs_root_name_to_be_absolute>::preferred_separator;
 
-template <Path_traits_kind Traits_kind, typename Char_type, Char_type Preferred_separator>
-basic_path<Traits_kind, Char_type, Preferred_separator>
-    *basic_path<Traits_kind, Char_type, Preferred_separator>::Parts::allocate(std::size_t count)
+template <detail::Path_traits_kind Traits_kind,
+          typename Char_type,
+          Char_type Preferred_separator,
+          bool Needs_root_name_to_be_absolute>
+basic_path<Traits_kind, Char_type, Preferred_separator, Needs_root_name_to_be_absolute>
+    *basic_path<Traits_kind, Char_type, Preferred_separator, Needs_root_name_to_be_absolute>::
+        Parts::allocate(std::size_t count)
 {
     if(count == 0)
         return nullptr;
-    return std::allocator<basic_path>::allocate(count);
+    return std::allocator<basic_path>().allocate(count);
 }
 
-template <Path_traits_kind Traits_kind, typename Char_type, Char_type Preferred_separator>
-void basic_path<Traits_kind, Char_type, Preferred_separator>::Parts::deallocate(
-    basic_path *values, std::size_t count) noexcept
+template <detail::Path_traits_kind Traits_kind,
+          typename Char_type,
+          Char_type Preferred_separator,
+          bool Needs_root_name_to_be_absolute>
+void basic_path<Traits_kind, Char_type, Preferred_separator, Needs_root_name_to_be_absolute>::
+    Parts::deallocate(basic_path *values, std::size_t count) noexcept
 {
     if(count != 0)
-        std::allocator<basic_path>::deallocate(values, count);
-}
+        std::allocator<basic_path>().deallocate(values, count);
 }
+
+typedef basic_path<> path;
 }
 }
 }
index 3ea1245587804383c5bc3c9ec1956e520f74573b..7e9dab59e6c1d7ee314650bcd07c78b8a4c48318 100644 (file)
@@ -55,7 +55,7 @@ typename std::char_traits<char32_t>::int_type decode_utf8(
                                         && noexcept(iter == sentinel ? 0 : 0))
 {
     if(iter == sentinel)
-        return error_value;
+        return std::char_traits<char32_t>::eof();
     auto byte0 = static_cast<std::uint8_t>(static_cast<char>(*iter));
     ++iter;
     if(byte0 < 0x80)
@@ -119,7 +119,7 @@ struct Encoded_character final
     static_assert(max_Chars != 0, "");
     Char_type chars[max_Chars];
     std::size_t used;
-    Char_type &front()
+    constexpr Char_type &front()
     {
         return chars[0];
     }
@@ -127,7 +127,7 @@ struct Encoded_character final
     {
         return chars[0];
     }
-    Char_type &back()
+    constexpr Char_type &back()
     {
         return chars[0];
     }
@@ -153,11 +153,11 @@ struct Encoded_character final
     {
         return begin() + used;
     }
-    iterator begin()
+    constexpr iterator begin()
     {
         return &chars[0];
     }
-    iterator end()
+    constexpr iterator end()
     {
         return begin() + used;
     }
@@ -171,9 +171,10 @@ struct Encoded_character final
     }
     constexpr const Char_type &operator[](std::size_t index) const
     {
-        return (assert(index < used), chars[index]);
+        assert(index < used);
+        return chars[index];
     }
-    Char_type &operator[](std::size_t index)
+    constexpr Char_type &operator[](std::size_t index)
     {
         assert(index < used);
         return chars[index];
@@ -261,7 +262,7 @@ typename std::char_traits<char32_t>::int_type decode_utf16(
                                         && noexcept(iter == sentinel ? 0 : 0))
 {
     if(iter == sentinel)
-        return error_value;
+        return std::char_traits<char32_t>::eof();
     auto unit0 = static_cast<std::uint16_t>(static_cast<char16_t>(*iter));
     ++iter;
     if(unit0 >= 0xD800U && unit0 < 0xDC00U)
@@ -296,7 +297,7 @@ typename std::char_traits<char32_t>::int_type decode_utf32(
                                         && noexcept(iter == sentinel ? 0 : 0))
 {
     if(iter == sentinel)
-        return error_value;
+        return std::char_traits<char32_t>::eof();
     auto retval = static_cast<std::uint32_t>(static_cast<char32_t>(*iter));
     ++iter;
     if(retval > 0x10FFFFUL)
@@ -408,9 +409,9 @@ template <typename Char_type>
 struct Decode_encode_functions
 {
     template <typename Input_iterator, typename Sentinel>
-    typename std::char_traits<char32_t>::int_type decode(
+    static typename std::char_traits<char32_t>::int_type decode(
         Input_iterator &iter, Sentinel sentinel, const Convert_options &convert_options) = delete;
-    Encoded_character<Char_type, 1> encode(
+    static Encoded_character<Char_type, 1> encode(
         char32_t ch, const Convert_options &convert_options) noexcept = delete;
 };
 
@@ -418,7 +419,7 @@ template <>
 struct Decode_encode_functions<char>
 {
     template <typename Input_iterator, typename Sentinel>
-    typename std::char_traits<char32_t>::int_type decode(
+    static typename std::char_traits<char32_t>::int_type decode(
         Input_iterator &iter,
         Sentinel sentinel,
         const Convert_options
@@ -431,7 +432,8 @@ struct Decode_encode_functions<char>
                            convert_options.allow_2_byte_null,
                            convert_options.error_value);
     }
-    Encoded_character<char, 4> encode(char32_t ch, const Convert_options &convert_options) noexcept
+    static Encoded_character<char, 4> encode(char32_t ch,
+                                             const Convert_options &convert_options) noexcept
     {
         return encode_utf8(ch, convert_options.use_2_byte_null);
     }
@@ -441,7 +443,7 @@ template <>
 struct Decode_encode_functions<char16_t>
 {
     template <typename Input_iterator, typename Sentinel>
-    typename std::char_traits<char32_t>::int_type decode(
+    static typename std::char_traits<char32_t>::int_type decode(
         Input_iterator &iter,
         Sentinel sentinel,
         const Convert_options
@@ -453,8 +455,8 @@ struct Decode_encode_functions<char16_t>
                             convert_options.allow_unpaired_surrogate_code_points,
                             convert_options.error_value);
     }
-    Encoded_character<char16_t, 2> encode(char32_t ch,
-                                          const Convert_options &convert_options) noexcept
+    static Encoded_character<char16_t, 2> encode(char32_t ch,
+                                                 const Convert_options &convert_options) noexcept
     {
         return encode_utf16(ch);
     }
@@ -464,7 +466,7 @@ template <>
 struct Decode_encode_functions<char32_t>
 {
     template <typename Input_iterator, typename Sentinel>
-    typename std::char_traits<char32_t>::int_type decode(
+    static typename std::char_traits<char32_t>::int_type decode(
         Input_iterator &iter,
         Sentinel sentinel,
         const Convert_options
@@ -476,8 +478,8 @@ struct Decode_encode_functions<char32_t>
                             convert_options.allow_unpaired_surrogate_code_points,
                             convert_options.error_value);
     }
-    Encoded_character<char32_t, 1> encode(char32_t ch,
-                                          const Convert_options &convert_options) noexcept
+    static Encoded_character<char32_t, 1> encode(char32_t ch,
+                                                 const Convert_options &convert_options) noexcept
     {
         return encode_utf32(ch);
     }
@@ -487,7 +489,7 @@ template <>
 struct Decode_encode_functions<wchar_t>
 {
     template <typename Input_iterator, typename Sentinel>
-    typename std::char_traits<char32_t>::int_type decode(
+    static typename std::char_traits<char32_t>::int_type decode(
         Input_iterator &iter,
         Sentinel sentinel,
         const Convert_options
@@ -499,8 +501,8 @@ struct Decode_encode_functions<wchar_t>
                            convert_options.allow_unpaired_surrogate_code_points,
                            convert_options.error_value);
     }
-    Encoded_character<wchar_t, 2> encode(char32_t ch,
-                                         const Convert_options &convert_options) noexcept
+    static Encoded_character<wchar_t, 2> encode(char32_t ch,
+                                                const Convert_options &convert_options) noexcept
     {
         return encode_wide(ch);
     }
@@ -568,6 +570,5 @@ Target string_cast(basic_string_view<Source_Char_type, Source_Traits> source)
 }
 }
 }
-}
 
 #endif /* UTIL_TEXT_H_ */
diff --git a/src/util/util_test.cpp b/src/util/util_test.cpp
new file mode 100644 (file)
index 0000000..80a4c47
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017 Jacob Lifshay
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+int main()
+{
+    // tests called in static initializers
+}