diff options
Diffstat (limited to 'include/llfio/v2.0/path_view.hpp')
-rw-r--r-- | include/llfio/v2.0/path_view.hpp | 352 |
1 files changed, 229 insertions, 123 deletions
diff --git a/include/llfio/v2.0/path_view.hpp b/include/llfio/v2.0/path_view.hpp index 48618e3d..f5ae2044 100644 --- a/include/llfio/v2.0/path_view.hpp +++ b/include/llfio/v2.0/path_view.hpp @@ -1,5 +1,5 @@ /* A view of a path to something -(C) 2017-2020 Niall Douglas <http://www.nedproductions.biz/> (20 commits) +(C) 2017-2021 Niall Douglas <http://www.nedproductions.biz/> (20 commits) File Created: Jul 2017 @@ -215,7 +215,7 @@ public: //! i.e. `is_source_chartype_acceptable`, or is `byte` template <class Char> static constexpr bool is_source_acceptable = detail::is_source_acceptable<Char>::value; - //! The default internal buffer size used by `c_str`. + //! The default internal buffer size used by `rendered_path`. static constexpr size_t default_internal_buffer_size = 1024; // 2Kb for wchar_t, 1Kb for char //! How to interpret separators @@ -236,7 +236,7 @@ public: }; //! The default deleter to use - template <class T> using default_c_str_deleter = std::default_delete<T>; + template <class T> using default_rendered_path_deleter = std::default_delete<T>; private: static constexpr auto _npos = string_view::npos; @@ -718,24 +718,24 @@ private: { return a.compare(b); } - // Disparate source encodings compare via c_str + // Disparate source encodings compare via rendered_path template <class DestT, class Deleter, size_t _internal_buffer_size, class Char1T, class Char2T> static int _compare(basic_string_view<Char1T> a, enum zero_termination a_zt, basic_string_view<Char2T> b, enum zero_termination b_zt, const std::locale *loc) noexcept { path_view_component _a_(a.data(), a.size(), a_zt); path_view_component _b_(b.data(), b.size(), b_zt); - c_str<DestT, Deleter, _internal_buffer_size> _a(_a_, not_zero_terminated, loc); - c_str<DestT, Deleter, _internal_buffer_size> _b(_b_, not_zero_terminated, loc); - if(_a.length < _b.length) + rendered_path<zero_termination::not_zero_terminated, DestT, Deleter, _internal_buffer_size> _a(_a_, loc); + rendered_path<zero_termination::not_zero_terminated, DestT, Deleter, _internal_buffer_size> _b(_b_, loc); + if(_a.size() < _b.size()) { return -1; } - if(_a.length > _b.length) + if(_a.size() > _b.size()) { return 1; } - return _do_compare(_a.buffer, _b.buffer, _a.length); + return _do_compare(_a.data(), _b.data(), _a.size()); } public: @@ -772,17 +772,17 @@ public: If the source encodings of the two path views are compatible, a lexicographical comparison is performed. If they are incompatible, either or both views are converted to the destination encoding - using `c_str<T, Delete, _internal_buffer_size>`, and then a + using `rendered_path<T, Delete, _internal_buffer_size>`, and then a lexicographical comparison is performed. This can, for obvious reasons, be expensive. It can also throw - exceptions, as `c_str` does. + exceptions, as `rendered_path` does. If the destination encoding is `byte`, `memcmp()` is used, - and `c_str` is never invoked as the two sources are byte + and `rendered_path` is never invoked as the two sources are byte compared directly. */ - LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = default_c_str_deleter<T[]>, + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = default_rendered_path_deleter<T[]>, size_t _internal_buffer_size = default_internal_buffer_size) LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>)) constexpr int compare(path_view_component p, const std::locale &loc) const @@ -793,7 +793,7 @@ public: }); } //! \overload - LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = default_c_str_deleter<T[]>, + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = default_rendered_path_deleter<T[]>, size_t _internal_buffer_size = default_internal_buffer_size) LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>)) constexpr int compare(path_view_component p) const @@ -804,6 +804,118 @@ public: }); } +private: + template <class T> struct _rendered_path_base_ + { + protected: + using _view_type = span<const T>; + _view_type _ref; + + constexpr basic_string_view<T> _as_string_view() const { return {_ref.data(), _ref.size()}; } + + public: + //! Type of the value type + using value_type = T; + //! Type of the pointer type + using pointer = const T *; + //! Type of the const pointer type + using const_pointer = const T *; + //! Type of the reference type + using reference = const T &; + //! Type of the const reference type + using const_reference = const T &; + //! Type of the iterator type + using iterator = typename _view_type::iterator; + //! Type of the const iterator type + using const_iterator = typename _view_type::const_iterator; + //! Type of the reverse iterator type + using reverse_iterator = typename _view_type::reverse_iterator; + //! Type of the const reverse iterator type + using const_reverse_iterator = typename _view_type::const_reverse_iterator; + //! Type of the size type + using size_type = typename _view_type::size_type; + //! Type of the difference type + using difference_type = typename _view_type::difference_type; + + _rendered_path_base_() = default; + _rendered_path_base_(const _rendered_path_base_ &) = default; + _rendered_path_base_ &operator=(const _rendered_path_base_ &) = default; + ~_rendered_path_base_() = default; + + //! Begin iteration + constexpr iterator begin() { return _ref.begin(); } + //! Begin iteration + constexpr const_iterator begin() const { return _ref.begin(); } + //! Begin iteration + constexpr const_iterator cbegin() const { return _ref.cbegin(); } + //! End iteration + constexpr iterator end() { return _ref.end(); } + //! End iteration + constexpr const_iterator end() const { return _ref.end(); } + //! End iteration + constexpr const_iterator cend() const { return _ref.cend(); } + //! Begin reverse iteration + constexpr reverse_iterator rbegin() { return _ref.rbegin(); } + //! Begin reverse iteration + constexpr const_reverse_iterator rbegin() const { return _ref.rbegin(); } + //! Begin reverse iteration + constexpr const_reverse_iterator crbegin() const { return _ref.crbegin(); } + //! End reverse iteration + constexpr reverse_iterator rend() { return _ref.rend(); } + //! End reverse iteration + constexpr const_reverse_iterator rend() const { return _ref.rend(); } + //! End reverse iteration + constexpr const_reverse_iterator crend() const { return _ref.crend(); } + + //! Access + constexpr reference operator[](size_type idx) { return _ref[idx]; } + //! Access + constexpr const_reference operator[](size_type idx) const { return _ref[idx]; } + //! Access + constexpr reference at(size_type idx) { return _ref.at(idx); } + //! Access + constexpr const_reference at(size_type idx) const { return _ref.at(idx); } + //! Access + constexpr reference front() { return _ref.front(); } + //! Access + constexpr const_reference front() const { return _ref.front(); } + //! Access + constexpr reference back() { return _ref.back(); } + //! Access + constexpr const_reference back() const { return _ref.back(); } + //! Access + constexpr pointer data() { return _ref.data(); } + //! Access + constexpr const_pointer data() const { return _ref.data(); } + //! Size + constexpr size_type size() const { return _ref.size(); } + //! Size + constexpr size_type length() const { return _ref.length(); } + //! Max size + constexpr size_type max_size() const { return _ref.max_size(); } + //! Empty + QUICKCPPLIB_NODISCARD constexpr bool empty() const { return _ref.empty(); } + //! As span + constexpr _view_type as_span() const { return _ref; } + }; + template <enum path_view_component::zero_termination ZeroTermination, class T, bool = false> struct _rendered_path_base : public _rendered_path_base_<T> + { + //! As string view + constexpr basic_string_view<T> as_string_view() const { return {this->_ref.data(), this->_ref.size()}; } + }; + template <class T> struct _rendered_path_base<zero_terminated, T> : public _rendered_path_base_<T> + { + //! Return a zero-terminated character array suitable for use as a C string + constexpr const T *c_str() const noexcept { return this->_ref.data(); } + }; + template <bool _> struct _rendered_path_base<zero_terminated, byte, _> : public _rendered_path_base_<byte> + { + }; + template <bool _> struct _rendered_path_base<not_zero_terminated, byte, _> : public _rendered_path_base_<byte> + { + }; + +public: /*! Instantiate from a `path_view_component` to get a path suitable for feeding to other code. \tparam T The destination encoding required. \tparam Deleter A custom deleter OR STL allocator for any temporary buffer. @@ -820,34 +932,28 @@ public: and the source is not zero terminated, a straight memory copy is performed into the temporary buffer. - `c_str` contains a temporary buffer sized according to the template parameter. Output + `rendered_path` contains a temporary buffer sized according to the template parameter. Output below that amount involves no dynamic memory allocation. Output above that amount calls `operator new[]`. You can use an externally supplied larger temporary buffer to avoid dynamic memory allocation in all situations. */ - LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class AllocatorOrDeleter = default_c_str_deleter<T[]>, - size_t _internal_buffer_size = default_internal_buffer_size) + LLFIO_TEMPLATE(enum path_view_component::zero_termination ZeroTermination, class T = typename filesystem::path::value_type, + class AllocatorOrDeleter = default_rendered_path_deleter<T[]>, size_t _internal_buffer_size = default_internal_buffer_size) LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>)) - struct c_str + class rendered_path : public _rendered_path_base<ZeroTermination, T> { - static_assert(is_source_acceptable<T>, "path_view_component::c_str<T> does not have a T which is one of byte, char, wchar_t, char8_t nor char16_t"); + using _base = _rendered_path_base<ZeroTermination, T>; + static_assert(is_source_acceptable<T>, "path_view_component::rendered_path<T> does not have a T which is one of byte, char, wchar_t, char8_t nor char16_t"); template <class DestT, class _Deleter, size_t _internal_buffer_size_, class Char1T, class Char2T> friend int path_view_component::_compare(basic_string_view<Char1T> a, enum zero_termination a_zt, basic_string_view<Char2T> b, enum zero_termination b_zt, const std::locale *loc) noexcept; - //! Type of the value type - using value_type = T; + public: + using value_type = typename _base::value_type; //! Type of the allocator, or `void` if that was not configured using allocator_type = decltype(detail::is_allocator(std::declval<AllocatorOrDeleter>())); //! Type of the deleter, or `void` if that was not configured using deleter_type = decltype(detail::is_deleter<value_type>(std::declval<AllocatorOrDeleter>())); - //! The size of the internal temporary buffer - static constexpr size_t internal_buffer_size = (_internal_buffer_size == 0) ? 1 : _internal_buffer_size; - - //! Pointer to the possibly-converted path - const value_type *buffer{nullptr}; - //! Number of characters, excluding zero terminating char, at buffer - size_t length{0}; private: template <class X = void> static constexpr bool _is_deleter_based = std::is_void<allocator_type>::value; @@ -856,22 +962,21 @@ public: LLFIO_TEMPLATE(class U, class source_type) LLFIO_TREQUIRES(LLFIO_TPRED(!std::is_same<source_type, value_type>::value)) - void _make_passthrough(path_view_component /*unused*/, enum zero_termination /*unused*/, U & /*unused*/, const source_type * /*unused*/) + void _make_passthrough(path_view_component /*unused*/, U & /*unused*/, const source_type * /*unused*/) { LLFIO_LOG_FATAL(nullptr, "Passthrough to non-identity type ought to never be called!"); abort(); } - template <class U> void _make_passthrough(path_view_component view, enum zero_termination output_zero_termination, U &allocate, const value_type *source) + template <class U> void _make_passthrough(path_view_component view, U &allocate, const value_type *source) { using LLFIO_V2_NAMESPACE::basic_string_view; // If the consuming API is a NT kernel API, and we have / in the path, we shall need to do slash conversion const bool needs_slash_translation = (filesystem::path::preferred_separator != '/') && (view.formatting() == format::auto_format || view.formatting() == format::generic_format) && view._invoke([](auto sv) { return sv.find('/') != _npos; }); - length = view._length; - if(!needs_slash_translation && (output_zero_termination == not_zero_terminated || view._zero_terminated)) + if(!needs_slash_translation && (this->zero_termination() == not_zero_terminated || view._zero_terminated)) { - buffer = source; + _base::_ref = typename _base::_view_type(source, view._length); } else { @@ -891,30 +996,26 @@ public: // Use the internal buffer memcpy(_buffer, source, required_bytes); _buffer[required_length] = 0; - buffer = _buffer; + _base::_ref = typename _base::_view_type(_buffer, view._length); } else { auto *buffer_ = allocate(required_length); - if(nullptr == buffer_) - { - length = 0; - } - else + if(nullptr != buffer_) { _bytes_to_delete = required_bytes; memcpy(buffer_, source, required_bytes); buffer_[required_length] = 0; - buffer = buffer_; + _base::_ref = typename _base::_view_type(_buffer, view._length); } } if(needs_slash_translation) { - basic_string_view<value_type> sv(buffer, required_length); + auto sv = _base::_as_string_view(); auto idx = sv.find('/'); while(idx != _npos) { - const_cast<value_type *>(buffer)[idx] = filesystem::path::preferred_separator; + const_cast<value_type *>(sv.data())[idx] = filesystem::path::preferred_separator; idx = sv.find('/', idx); } } @@ -922,59 +1023,57 @@ public: } public: - constexpr c_str() {} + constexpr rendered_path() {} #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4127) // conditional expression is constant #endif private: - template <class U> void _init(path_view_component view, enum zero_termination output_zero_termination, const std::locale *loc, U &&allocate) + template <class U> void _init(path_view_component view, const std::locale *loc, U &&allocate) { if(0 == view._length) { _buffer[0] = 0; - buffer = _buffer; - length = 0; + _base::_ref = typename _base::_view_type(_buffer, size_t(0)); return; } if(std::is_same<T, byte>::value || view._passthrough) { - length = view._length; - buffer = (const value_type *) view._bytestr; + _base::_ref = typename _base::_view_type((value_type *) view._bytestr, view._length); return; } if(std::is_same<T, char>::value && view._char) { - _make_passthrough(view, output_zero_termination, allocate, view._charstr); + _make_passthrough(view, allocate, view._charstr); return; } if(std::is_same<T, wchar_t>::value && view._wchar) { - _make_passthrough(view, output_zero_termination, allocate, view._wcharstr); + _make_passthrough(view, allocate, view._wcharstr); return; } if(std::is_same<T, char8_t>::value && view._utf8) { - _make_passthrough(view, output_zero_termination, allocate, view._char8str); + _make_passthrough(view, allocate, view._char8str); return; } if(std::is_same<T, char16_t>::value && view._utf16) { - _make_passthrough(view, output_zero_termination, allocate, view._char16str); + _make_passthrough(view, allocate, view._char16str); return; } #ifdef _WIN32 // On Windows, consider char16_t input equivalent to wchar_t if(std::is_same<T, wchar_t>::value && view._utf16) { - _make_passthrough(view, output_zero_termination, allocate, view._wcharstr); + _make_passthrough(view, allocate, view._wcharstr); return; } #else // On POSIX, consider char8_t input equivalent to char if(std::is_same<T, char>::value && view._utf8) { - _make_passthrough(view, output_zero_termination, allocate, view._charstr); + _make_passthrough(view, allocate, view._charstr); return; } #endif @@ -1010,15 +1109,13 @@ public: if(0 == required_length) { // The internal buffer was sufficient. - buffer = _buffer; - length = end - _buffer; + _base::_ref = typename _base::_view_type(_buffer, end - _buffer); return; } // The internal buffer is too small. Fall back to dynamic allocation. This may throw. auto *allocated_buffer = allocate(required_length); if(nullptr == allocated_buffer) { - length = 0; return; } _bytes_to_delete = required_length * sizeof(value_type); @@ -1027,31 +1124,30 @@ public: end = allocated_buffer + (end - _buffer); if(view._passthrough) { - end = detail::reencode_path_to(length, end, required_length, view._bytestr, view._length, loc); + end = detail::reencode_path_to(view._length, end, required_length, view._bytestr, view._length, loc); } else if(view._char) { - end = detail::reencode_path_to(length, end, required_length, view._charstr, view._length, loc); + end = detail::reencode_path_to(view._length, end, required_length, view._charstr, view._length, loc); } else if(view._wchar) { - end = detail::reencode_path_to(length, end, required_length, view._wcharstr, view._length, loc); + end = detail::reencode_path_to(view._length, end, required_length, view._wcharstr, view._length, loc); } else if(view._utf8) { - end = detail::reencode_path_to(length, end, required_length, view._char8str, view._length, loc); + end = detail::reencode_path_to(view._length, end, required_length, view._char8str, view._length, loc); } else if(view._utf16) { - end = detail::reencode_path_to(length, end, required_length, view._char16str, view._length, loc); + end = detail::reencode_path_to(view._length, end, required_length, view._char16str, view._length, loc); } else { LLFIO_LOG_FATAL(nullptr, "path_view_component::cstr somehow sees no state!"); abort(); } - buffer = allocated_buffer; - length = end - buffer; + _base::_ref = typename _base::_view_type(allocated_buffer, end - allocated_buffer); } #ifdef _MSC_VER #pragma warning(pop) @@ -1121,11 +1217,11 @@ public: } // used by compare() - c_str(path_view_component view, enum zero_termination output_zero_termination, const std::locale *loc) + rendered_path(path_view_component view, const std::locale *loc) : _deleter1(_default_deleter) , _deleter1arg(&_deleter2) { - _init(view, output_zero_termination, loc, _default_allocate<AllocatorOrDeleter>(&_deleter2)); + _init(view, loc, _default_allocate<AllocatorOrDeleter>(&_deleter2)); } public: @@ -1135,7 +1231,7 @@ public: \param loc The locale to use to perform reencoding. \param allocate Either a callable with prototype `value_type *(size_t length)` which is defaulted to `return static_cast<value_type *>(::operator new[](length * sizeof(value_type)));`, - or a `pmr::memory_resource *`. You can return `nullptr` if you wish, the consumer of `c_str` will + or a `pmr::memory_resource *`. You can return `nullptr` if you wish, the consumer of `rendered_path` will see a `buffer` set to `nullptr`. If `loc` is defaulted, and an error occurs during any conversion from UTF-8 or UTF-16, an exception of @@ -1146,91 +1242,87 @@ public: */ LLFIO_TEMPLATE(class U, class V) LLFIO_TREQUIRES(LLFIO_TPRED(_is_deleter_based<U>), LLFIO_TEXPR(std::declval<U>()((size_t) 1)), LLFIO_TEXPR(std::declval<V>()((value_type *) nullptr))) - c_str(path_view_component view, enum zero_termination output_zero_termination, const std::locale &loc, U &&allocate, V &&deleter = AllocatorOrDeleter(), - _custom_callable_deleter_tag = {}) + rendered_path(path_view_component view, const std::locale &loc, U &&allocate, V &&deleter = AllocatorOrDeleter(), _custom_callable_deleter_tag = {}) : _deleter1(_invoke_deleter) , _deleter1arg(&_deleter2) , _deleter2(static_cast<V &&>(deleter)) { - _init(view, output_zero_termination, loc, static_cast<U &&>(allocate)); + _init(view, loc, static_cast<U &&>(allocate)); } //! \overload LLFIO_TEMPLATE(class U, class V) LLFIO_TREQUIRES(LLFIO_TPRED(_is_deleter_based<U>), LLFIO_TEXPR(std::declval<U>()((size_t) 1)), LLFIO_TEXPR(std::declval<V>()((value_type *) nullptr))) - c_str(path_view_component view, enum zero_termination output_zero_termination, U &&allocate, V &&deleter = AllocatorOrDeleter(), - _custom_callable_deleter_tag = {}) + rendered_path(path_view_component view, U &&allocate, V &&deleter = AllocatorOrDeleter(), _custom_callable_deleter_tag = {}) : _deleter1(_invoke_deleter) , _deleter1arg(&_deleter2) , _deleter2(static_cast<V &&>(deleter)) { - _init(view, output_zero_termination, (const std::locale *) nullptr, static_cast<U &&>(allocate)); + _init(view, (const std::locale *) nullptr, static_cast<U &&>(allocate)); } //! \overload memory_resource - c_str(path_view_component view, enum zero_termination output_zero_termination, const std::locale &loc, pmr::memory_resource &mr, _memory_resource_tag = {}) + rendered_path(path_view_component view, const std::locale &loc, pmr::memory_resource &mr, _memory_resource_tag = {}) : _deleter1(_memory_resouce_deallocate) , _deleter1arg(&mr) { - _init(view, output_zero_termination, &loc, _memory_resource_allocate{&mr}); + _init(view, &loc, _memory_resource_allocate{&mr}); } //! \overload memory_resource - c_str(path_view_component view, enum zero_termination output_zero_termination, pmr::memory_resource &mr, _memory_resource_tag = {}) + rendered_path(path_view_component view, pmr::memory_resource &mr, _memory_resource_tag = {}) : _deleter1(_memory_resouce_deallocate) , _deleter1arg(&mr) { - _init(view, output_zero_termination, (const std::locale *) nullptr, _memory_resource_allocate{&mr}); + _init(view, (const std::locale *) nullptr, _memory_resource_allocate{&mr}); } //! \overload STL allocator LLFIO_TEMPLATE(class U) LLFIO_TREQUIRES(LLFIO_TPRED(_is_allocator_based<U>), LLFIO_TEXPR(std::declval<U>().allocate((size_t) 1))) - c_str(path_view_component view, enum zero_termination output_zero_termination, const std::locale &loc, U &&allocate, _stl_allocator_tag = {}) + rendered_path(path_view_component view, const std::locale &loc, U &&allocate, _stl_allocator_tag = {}) : _deleter1(_stl_allocator_deallocate<allocator_type>) , _deleter1arg(&_deleter2) , _deleter2(static_cast<U &&>(allocate)) { - _init(view, output_zero_termination, &loc, _stl_allocator_allocate<std::decay_t<U>>(static_cast<std::decay_t<U> *>(&_deleter2))); + _init(view, &loc, _stl_allocator_allocate<std::decay_t<U>>(static_cast<std::decay_t<U> *>(&_deleter2))); } //! \overload STL allocator LLFIO_TEMPLATE(class U) LLFIO_TREQUIRES(LLFIO_TPRED(_is_allocator_based<U>), LLFIO_TEXPR(std::declval<U>().allocate((size_t) 1))) - c_str(path_view_component view, enum zero_termination output_zero_termination, U &&allocate, _stl_allocator_tag = {}) + rendered_path(path_view_component view, U &&allocate, _stl_allocator_tag = {}) : _deleter1(_stl_allocator_deallocate<allocator_type>) , _deleter1arg(&_deleter2) , _deleter2(static_cast<U &&>(allocate)) { - _init(view, output_zero_termination, (const std::locale *) nullptr, _stl_allocator_allocate<std::decay_t<U>>(static_cast<std::decay_t<U> *>(&_deleter2))); + _init(view, (const std::locale *) nullptr, _stl_allocator_allocate<std::decay_t<U>>(static_cast<std::decay_t<U> *>(&_deleter2))); } //! \overload default allocation - c_str(path_view_component view, enum zero_termination output_zero_termination, const std::locale &loc) + rendered_path(path_view_component view, const std::locale &loc) : _deleter1(_default_deleter) , _deleter1arg(&_deleter2) { - _init(view, output_zero_termination, &loc, _default_allocate<AllocatorOrDeleter>(&_deleter2)); + _init(view, &loc, _default_allocate<AllocatorOrDeleter>(&_deleter2)); } //! \overload - c_str(path_view_component view, enum zero_termination output_zero_termination) + explicit rendered_path(path_view_component view) : _deleter1(_default_deleter) , _deleter1arg(&_deleter2) { - _init(view, output_zero_termination, nullptr, _default_allocate<AllocatorOrDeleter>(&_deleter2)); + _init(view, nullptr, _default_allocate<AllocatorOrDeleter>(&_deleter2)); } - //! Construct from a compatible `c_str`. + //! Construct from a compatible `rendered_path`. LLFIO_TEMPLATE(class AllocatorOrDeleter2, size_t _internal_buffer_size2) - LLFIO_TREQUIRES(LLFIO_TPRED(!std::is_same<c_str, c_str<T, AllocatorOrDeleter2, _internal_buffer_size2>>::value), + LLFIO_TREQUIRES(LLFIO_TPRED(!std::is_same<rendered_path, rendered_path<ZeroTermination, T, AllocatorOrDeleter2, _internal_buffer_size2>>::value), LLFIO_TPRED(std::is_constructible<AllocatorOrDeleter, AllocatorOrDeleter2>::value)) - explicit c_str(c_str<T, AllocatorOrDeleter2, _internal_buffer_size2> &&o) noexcept - : buffer(o.buffer) - , length(o.length) + explicit rendered_path(rendered_path<ZeroTermination, T, AllocatorOrDeleter2, _internal_buffer_size2> &&o) noexcept + : _base(o) , _bytes_to_delete(o._bytes_to_delete) , _deleter1(o._deleter1) , _deleter1arg(o._deleter1arg) , _deleter2(std::move(o._deleter2)) { } - ~c_str() { reset(); } - c_str(const c_str &) = delete; - c_str(c_str &&o) noexcept - : buffer(o.buffer) - , length(o.length) + ~rendered_path() { reset(); } + rendered_path(const rendered_path &) = delete; + rendered_path(rendered_path &&o) noexcept + : _base(o) , _bytes_to_delete(o._bytes_to_delete) , _deleter1(o._deleter1) , _deleter1arg(o._deleter1arg) @@ -1238,30 +1330,30 @@ public: { if(this != &o) { - if(o.buffer == o._buffer) + if(o.data() == o._buffer) { - memcpy(_buffer, o._buffer, (o.length + 1) * sizeof(value_type)); - buffer = _buffer; + memcpy(_buffer, o._buffer, (o.size() + 1) * sizeof(value_type)); + _base::_ref = typename _base::_view_type(_buffer, _base::_ref.size()); } if(o._deleter1arg == &o._deleter2) { _deleter1arg = &_deleter2; } - o.buffer = nullptr; + o._ref = {}; o._bytes_to_delete = 0; o._deleter1 = nullptr; o._deleter1arg = nullptr; } } - c_str &operator=(const c_str &) = delete; - c_str &operator=(c_str &&o) noexcept + rendered_path &operator=(const rendered_path &) = delete; + rendered_path &operator=(rendered_path &&o) noexcept { if(this == &o) { return *this; } - this->~c_str(); - new(this) c_str(std::move(o)); + this->~rendered_path(); + new(this) rendered_path(std::move(o)); return *this; } @@ -1270,9 +1362,8 @@ public: { if(_bytes_to_delete > 0) { - _deleter1(_deleter1arg, const_cast<value_type *>(buffer), _bytes_to_delete); - buffer = nullptr; - length = 0; + _deleter1(_deleter1arg, const_cast<value_type *>(_base::_ref.data()), _bytes_to_delete); + _base::_ref = typename _base::_view_type{}; _bytes_to_delete = 0; } } @@ -1281,15 +1372,21 @@ public: { if(_bytes_to_delete > 0) { - auto *ret = buffer; - buffer = nullptr; - length = 0; + auto *ret = _base::_ref.data(); + _base::_ref = typename _base::_view_type{}; _bytes_to_delete = 0; return ret; } return nullptr; } + //! The zero termination of this rendered path + static constexpr enum zero_termination zero_termination() noexcept { return ZeroTermination; } + //! The size of the internal buffer + static constexpr size_t internal_buffer_size() noexcept { return (_internal_buffer_size > 0) ? _internal_buffer_size : 1; } + //! The storage capacity, which may be larger than `size()` if the internal buffer is in use + size_t capacity() noexcept { return (this->data() == _buffer) ? internal_buffer_size() : this->size(); } + //! Access the custom deleter instance passed to the constructor const AllocatorOrDeleter &deleter() const noexcept { return _deleter2; } //! Access the custom deleter instance passed to the constructor @@ -1310,17 +1407,26 @@ public: AllocatorOrDeleter _deleter2; // MAKE SURE this is the final item in storage, the compiler will elide the storage // under optimisation if it can prove it is never used. - value_type _buffer[internal_buffer_size]{}; + value_type _buffer[internal_buffer_size()]{}; }; + //! Convenience type alias + template <class T = typename filesystem::path::value_type, class AllocatorOrDeleter = default_rendered_path_deleter<T[]>, + size_t _internal_buffer_size = default_internal_buffer_size> + using zero_terminated_rendered_path = rendered_path<zero_termination::zero_terminated, T, AllocatorOrDeleter, _internal_buffer_size>; + //! Convenience type alias + template <class T = typename filesystem::path::value_type, class AllocatorOrDeleter = default_rendered_path_deleter<T[]>, + size_t _internal_buffer_size = default_internal_buffer_size> + using not_zero_terminated_rendered_path = rendered_path<zero_termination::not_zero_terminated, T, AllocatorOrDeleter, _internal_buffer_size>; #ifdef __cpp_concepts - template <class T, class Deleter, size_t _internal_buffer_size> + template <enum path_view_component::zero_termination ZeroTermination, class T, class Deleter, size_t _internal_buffer_size> requires(is_source_acceptable<T>) #elif defined(_MSC_VER) - template <class T, class Deleter, size_t _internal_buffer_size, class> + template <enum path_view_component::zero_termination ZeroTermination, class T, class Deleter, size_t _internal_buffer_size, class> #else - template <class T, class Deleter, size_t _internal_buffer_size, typename std::enable_if<(is_source_acceptable<T>), bool>::type> + template <enum path_view_component::zero_termination ZeroTermination, class T, class Deleter, size_t _internal_buffer_size, + typename std::enable_if<(is_source_acceptable<T>), bool>::type> #endif - friend struct c_str; + friend class rendered_path; }; static_assert(std::is_trivially_copyable<path_view_component>::value, "path_view_component is not trivially copyable!"); static_assert(sizeof(path_view_component) == 3 * sizeof(void *), "path_view_component is not three pointers in size!"); @@ -2041,22 +2147,22 @@ public: If the source encodings of the two path views are compatible, a lexicographical comparison is performed. If they are incompatible, either or both views are converted to the destination encoding - using `c_str<T, Delete, _internal_buffer_size>`, and then a + using `rendered_path<T, Delete, _internal_buffer_size>`, and then a lexicographical comparison is performed. This can, for obvious reasons, be expensive. It can also throw - exceptions, as `c_str` does. + exceptions, as `rendered_path` does. If the destination encoding is `byte`, `memcmp()` is used, - and `c_str` is never invoked as the two sources are byte + and `rendered_path` is never invoked as the two sources are byte compared directly. */ - LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = default_c_str_deleter<T[]>, + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = default_rendered_path_deleter<T[]>, size_t _internal_buffer_size = default_internal_buffer_size) LLFIO_TREQUIRES(LLFIO_TPRED(path_view::is_source_acceptable<T>)) constexpr inline int compare(path_view p, const std::locale &loc) const; //! \overload - LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = default_c_str_deleter<T[]>, + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = default_rendered_path_deleter<T[]>, size_t _internal_buffer_size = default_internal_buffer_size) LLFIO_TREQUIRES(LLFIO_TPRED(path_view::is_source_acceptable<T>)) constexpr inline int compare(path_view p) const; @@ -2385,16 +2491,16 @@ constexpr inline int path_view::compare(path_view o) const //! Append a path view component to a path inline filesystem::path &operator+=(filesystem::path &a, path_view_component b) { - path_view_component::c_str<> zpath(b, path_view_component::not_zero_terminated); - basic_string_view<filesystem::path::value_type> _(zpath.buffer, zpath.length); + path_view_component::not_zero_terminated_rendered_path<> zpath(b); + basic_string_view<filesystem::path::value_type> _ = zpath.as_string_view(); a.concat(_.begin(), _.end()); return a; } //! Append a path view component to a path inline filesystem::path &operator/=(filesystem::path &a, path_view_component b) { - path_view_component::c_str<> zpath(b, path_view_component::not_zero_terminated); - basic_string_view<filesystem::path::value_type> _(zpath.buffer, zpath.length); + path_view_component::not_zero_terminated_rendered_path<> zpath(b); + basic_string_view<filesystem::path::value_type> _ = zpath.as_string_view(); a.append(_.begin(), _.end()); return a; } |