// // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) // Copyright (c) 2019-2020 Krystian Stasiowski (sdkrystian at gmail dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // Official repository: https://github.com/boostorg/static_string // #ifndef BOOST_STATIC_STRING_DETAIL_STATIC_STRING_HPP #define BOOST_STATIC_STRING_DETAIL_STATIC_STRING_HPP #include #include #include #include #include namespace boost { namespace static_string { template class basic_static_string; namespace detail { template using static_string = basic_static_string>; template using static_wstring = basic_static_string>; // Find the smallest width integral type that can hold a value as large as N (Glen Fernandes) template using smallest_width = typename std::conditional<(N <= (std::numeric_limits::max)()), unsigned char, typename std::conditional<(N <= (std::numeric_limits::max)()), unsigned short, typename std::conditional<(N <= (std::numeric_limits::max)()), unsigned int, typename std::conditional<(N <= (std::numeric_limits::max)()), unsigned long, typename std::conditional<(N <= (std::numeric_limits::max)()), unsigned long long, void>::type>::type>::type>::type>::type; // std::is_nothrow_convertible is C++20 template void is_nothrow_convertible_helper(To) noexcept; // MSVC is unable to parse this as a single expression, so a helper is needed template(std::declval()))> struct is_nothrow_convertible_msvc_helper { static const bool value = noexcept(is_nothrow_convertible_helper(std::declval())); }; template struct is_nothrow_convertible : std::false_type { }; template struct is_nothrow_convertible::value>::type> : std::true_type { }; // GCC 4.8, 4.9 workaround for void_t to make the defining-type-id dependant template struct void_t_helper { using type = void; }; // void_t for c++11 template using void_t = typename void_t_helper::type; // Simplified check for if a type is an iterator template struct is_iterator : std::false_type { }; template struct is_iterator::value, void_t>::type> : std::true_type { }; template struct is_iterator : std::true_type { }; template struct is_input_iterator : std::false_type { }; template struct is_input_iterator::value && std::is_convertible::iterator_category, std::input_iterator_tag>::value>::type> : std::true_type { }; template struct is_forward_iterator : std::false_type { }; template struct is_forward_iterator::value && std::is_convertible::iterator_category, std::forward_iterator_tag>::value>::type> : std::true_type { }; template struct is_subtractable : std::false_type { }; template struct is_subtractable() - std::declval())>> : std::true_type { }; // constexpr distance for c++14 template::value>::type* = nullptr> BOOST_STATIC_STRING_CPP14_CONSTEXPR typename std::iterator_traits::difference_type distance(InputIt first, InputIt last) { std::size_t dist = 0; for (; first != last; ++first, ++dist); return dist; } template::value>::type* = nullptr> BOOST_STATIC_STRING_CPP14_CONSTEXPR typename std::iterator_traits::difference_type distance(RandomIt first, RandomIt last) { return last - first; } // Copy using traits, respecting iterator rules template BOOST_STATIC_STRING_CPP14_CONSTEXPR void copy_with_traits(InputIt first, InputIt last, CharT* out) { for (; first != last; ++first, ++out) Traits::assign(*out, *first); } // Optimization for using the smallest possible type template class static_string_base_zero { public: BOOST_STATIC_STRING_CPP11_CONSTEXPR static_string_base_zero() noexcept { }; BOOST_STATIC_STRING_CPP11_CONSTEXPR static_string_base_zero(std::size_t n) noexcept : size_(n) { } BOOST_STATIC_STRING_CPP14_CONSTEXPR CharT* data_impl() noexcept { return data_; } BOOST_STATIC_STRING_CPP14_CONSTEXPR CharT const* data_impl() const noexcept { return data_; } BOOST_STATIC_STRING_CPP11_CONSTEXPR std::size_t size_impl() const noexcept { return size_; } BOOST_STATIC_STRING_CPP14_CONSTEXPR std::size_t set_size(std::size_t n) noexcept { return size_ = n; } BOOST_STATIC_STRING_CPP14_CONSTEXPR void term_impl() noexcept { Traits::assign(data_[size_], CharT()); } smallest_width size_{0}; #ifdef BOOST_STATIC_STRING_ALLOW_UNINIT_MEM CharT data_[N + 1]; #else CharT data_[N + 1]{}; #endif }; // Optimization for when the size is 0 template class static_string_base_zero<0, CharT, Traits> { public: BOOST_STATIC_STRING_CPP11_CONSTEXPR static_string_base_zero() noexcept { } BOOST_STATIC_STRING_CPP11_CONSTEXPR static_string_base_zero(std::size_t) noexcept { } // Modifying the null terminator is UB BOOST_STATIC_STRING_CPP11_CONSTEXPR CharT* data_impl() const noexcept { return const_cast(&null_); } BOOST_STATIC_STRING_CPP11_CONSTEXPR std::size_t size_impl() const noexcept { return 0; } BOOST_STATIC_STRING_CPP11_CONSTEXPR std::size_t set_size(std::size_t) noexcept { return 0; } BOOST_STATIC_STRING_CPP14_CONSTEXPR void term_impl() noexcept { } private: static constexpr CharT null_{}; }; template constexpr CharT static_string_base_zero<0, CharT, Traits>::null_; #ifdef BOOST_STATIC_STRING_NULL_OPTIMIZATION // Optimization for storing the size in the last element template class static_string_base_null { public: BOOST_STATIC_STRING_CPP14_CONSTEXPR static_string_base_null() noexcept { set_size(0); } BOOST_STATIC_STRING_CPP14_CONSTEXPR static_string_base_null(std::size_t n) noexcept { set_size(n); } BOOST_STATIC_STRING_CPP14_CONSTEXPR CharT* data_impl() noexcept { return data_; } BOOST_STATIC_STRING_CPP14_CONSTEXPR CharT const* data_impl() const noexcept { return data_; } BOOST_STATIC_STRING_CPP11_CONSTEXPR std::size_t size_impl() const noexcept { return N - data_[N]; } BOOST_STATIC_STRING_CPP14_CONSTEXPR std::size_t set_size(std::size_t n) noexcept { return data_[N] = (N - n); } BOOST_STATIC_STRING_CPP14_CONSTEXPR void term_impl() noexcept { // This requires the null terminator to be 0 Traits::assign(data_[size_impl()], 0); } #ifdef BOOST_STATIC_STRING_ALLOW_UNINIT_MEM CharT data_[N + 1]; #else CharT data_[N + 1]{}; #endif }; #endif // Decides which size optimization to use // If the size is zero, the object will have no members // Otherwise, if CharT can hold the max size of the string, store the size in the last char // Otherwise, store the size of the string using a member of the smallest type possible template using optimization_base = #ifdef BOOST_STATIC_STRING_USE_NULL_OPTIMIZATION typename std::conditional<(N <= (std::numeric_limits::max)()) && (N != 0), static_string_base_null, static_string_base_zero>::type; #else static_string_base_zero; #endif template BOOST_STATIC_STRING_CPP14_CONSTEXPR inline int lexicographical_compare( CharT const* s1, std::size_t n1, CharT const* s2, std::size_t n2) noexcept { if(n1 < n2) return Traits::compare( s1, s2, n1) <= 0 ? -1 : 1; if(n1 > n2) return Traits::compare( s1, s2, n2) >= 0 ? 1 : -1; return Traits::compare(s1, s2, n1); } template BOOST_STATIC_STRING_CPP14_CONSTEXPR inline int lexicographical_compare( basic_string_view s1, CharT const* s2, std::size_t n2) noexcept { return detail::lexicographical_compare< CharT, Traits>(s1.data(), s1.size(), s2, n2); } template BOOST_STATIC_STRING_CPP14_CONSTEXPR inline int lexicographical_compare( const basic_static_string& s1, CharT const* s2, std::size_t n2) noexcept { return detail::lexicographical_compare< CharT, Traits>(s1.data(), s1.size(), s2, n2); } template BOOST_STATIC_STRING_CPP14_CONSTEXPR inline int lexicographical_compare( basic_string_view s1, basic_string_view s2) noexcept { return detail::lexicographical_compare( s1.data(), s1.size(), s2.data(), s2.size()); } template BOOST_STATIC_STRING_CPP14_CONSTEXPR inline int lexicographical_compare( const basic_static_string& s1, const basic_static_string& s2) noexcept { return detail::lexicographical_compare( s1.data(), s1.size(), s2.data(), s2.size()); } template inline char* integer_to_string( char* str_end, Integer value, std::true_type) noexcept { if (value == 0) { Traits::assign(*--str_end, '0'); return str_end; } if (value < 0) { value = -value; for (; value > 0; value /= 10) Traits::assign(*--str_end, "0123456789"[value % 10]); Traits::assign(*--str_end, '-'); return str_end; } for (; value > 0; value /= 10) Traits::assign(*--str_end, "0123456789"[value % 10]); return str_end; } template inline char* integer_to_string( char* str_end, Integer value, std::false_type) noexcept { if (value == 0) { Traits::assign(*--str_end, '0'); return str_end; } for (; value > 0; value /= 10) Traits::assign(*--str_end, "0123456789"[value % 10]); return str_end; } template inline wchar_t* integer_to_wstring( wchar_t* str_end, Integer value, std::true_type) noexcept { if (value == 0) { Traits::assign(*--str_end, L'0'); return str_end; } if (value < 0) { value = -value; for (; value > 0; value /= 10) Traits::assign(*--str_end, L"0123456789"[value % 10]); Traits::assign(*--str_end, L'-'); return str_end; } for (; value > 0; value /= 10) Traits::assign(*--str_end, L"0123456789"[value % 10]); return str_end; } template inline wchar_t* integer_to_wstring( wchar_t* str_end, Integer value, std::false_type) noexcept { if (value == 0) { Traits::assign(*--str_end, L'0'); return str_end; } for (; value > 0; value /= 10) Traits::assign(*--str_end, L"0123456789"[value % 10]); return str_end; } template inline static_string to_static_string_int_impl(Integer value) noexcept { char buffer[N]; const auto digits_end = std::end(buffer); const auto digits_begin = integer_to_string, Integer>( digits_end, value, std::is_signed{}); return static_string(digits_begin, std::distance(digits_begin, digits_end)); } template inline static_wstring to_static_wstring_int_impl(Integer value) noexcept { wchar_t buffer[N]; const auto digits_end = std::end(buffer); const auto digits_begin = integer_to_wstring, Integer>( digits_end, value, std::is_signed{}); return static_wstring(digits_begin, std::distance(digits_begin, digits_end)); } template inline static_string to_static_string_float_impl(Floating value) noexcept { // extra one needed for null terminator char buffer[N + 1]; std::sprintf(buffer, "%f", value); // this will not throw return static_string(buffer); } template inline static_wstring to_static_wstring_float_impl(Floating value) noexcept { // extra one needed for null terminator wchar_t buffer[N + 1]; std::swprintf(buffer, N + 1, L"%f", value); // this will not throw return static_wstring(buffer); } template< typename Traits, typename CharT, typename ForwardIterator> BOOST_STATIC_STRING_CPP14_CONSTEXPR inline ForwardIterator find_not_of( ForwardIterator first, ForwardIterator last, const CharT* str, std::size_t n) noexcept { for (; first != last; ++first) if (!Traits::find(str, n, *first)) return first; return last; } // constexpr search for C++14 template< typename ForwardIt1, typename ForwardIt2, typename BinaryPredicate> BOOST_STATIC_STRING_CPP14_CONSTEXPR inline ForwardIt1 search( ForwardIt1 first, ForwardIt1 last, ForwardIt2 s_first, ForwardIt2 s_last, BinaryPredicate p) { for (; ; ++first) { ForwardIt1 it = first; for (ForwardIt2 s_it = s_first; ; ++it, ++s_it) { if (s_it == s_last) return first; if (it == last) return last; if (!p(*it, *s_it)) break; } } } template< typename InputIt, typename ForwardIt, typename BinaryPredicate> BOOST_STATIC_STRING_CPP14_CONSTEXPR inline InputIt find_first_of( InputIt first, InputIt last, ForwardIt s_first, ForwardIt s_last, BinaryPredicate p) { for (; first != last; ++first) for (ForwardIt it = s_first; it != s_last; ++it) if (p(*first, *it)) return first; return last; } // Check if a pointer lies within a range without unspecified behavior, allowing it to be used in a constant evaluation template BOOST_STATIC_STRING_CPP14_CONSTEXPR inline bool is_inside( const T* src_first, const T* src_last, const T* ptr) { for (; src_first != src_last; ++src_first) if (src_first == ptr) return true; return false; } } // detail } // static_string } // boost #endif