mirror of https://github.com/trapexit/mergerfs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2311 lines
63 KiB
2311 lines
63 KiB
//
|
|
// Copyright (c) 2021-2021 Martin Moene
|
|
//
|
|
// https://github.com/martinmoene/string-lite
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
#ifndef NONSTD_STRING_LITE_HPP
|
|
#define NONSTD_STRING_LITE_HPP
|
|
|
|
#define string_lite_MAJOR 0
|
|
#define string_lite_MINOR 0
|
|
#define string_lite_PATCH 0
|
|
|
|
#define string_lite_VERSION string_STRINGIFY(string_lite_MAJOR) "." string_STRINGIFY(string_lite_MINOR) "." string_STRINGIFY(string_lite_PATCH)
|
|
|
|
#define string_STRINGIFY( x ) string_STRINGIFY_( x )
|
|
#define string_STRINGIFY_( x ) #x
|
|
|
|
// tweak header support:
|
|
|
|
#ifdef __has_include
|
|
# if __has_include(<nonstd/string.tweak.hpp>)
|
|
# include <nonstd/string.tweak.hpp>
|
|
# endif
|
|
#define string_HAVE_TWEAK_HEADER 1
|
|
#else
|
|
#define string_HAVE_TWEAK_HEADER 0
|
|
//# pragma message("string.hpp: Note: Tweak header not supported.")
|
|
#endif
|
|
|
|
#ifdef __has_include
|
|
# if __has_include( <string_view> )
|
|
# define string_HAVE_STD_STRING_VIEW 1
|
|
# else
|
|
# define string_HAVE_STD_STRING_VIEW 0
|
|
# endif
|
|
#else
|
|
# define string_HAVE_STD_STRING_VIEW 0
|
|
#endif
|
|
|
|
// Control presence of exception handling (try and auto discover):
|
|
|
|
#ifndef string_CONFIG_NO_EXCEPTIONS
|
|
# if defined(_MSC_VER)
|
|
# include <cstddef> // for _HAS_EXCEPTIONS
|
|
# endif
|
|
# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS)
|
|
# define string_CONFIG_NO_EXCEPTIONS 0
|
|
# else
|
|
# define string_CONFIG_NO_EXCEPTIONS 1
|
|
# endif
|
|
#endif
|
|
|
|
// TODO Switch between various versions of string_view:
|
|
// - minimal version: nonstd::string::string_view
|
|
// - lite version: nonstd::string_view
|
|
// - std version: std::string_view
|
|
|
|
#define string_CONFIG_SELECT_STRING_VIEW_INTERNAL 1
|
|
#define string_CONFIG_SELECT_STRING_VIEW_NONSTD 2
|
|
#define string_CONFIG_SELECT_STRING_VIEW_STD 3
|
|
|
|
#ifndef string_CONFIG_SELECT_STRING_VIEW
|
|
# define string_CONFIG_SELECT_STRING_VIEW string_CONFIG_SELECT_STRING_VIEW_INTERNAL
|
|
#endif
|
|
|
|
#if string_CONFIG_SELECT_STRING_VIEW==string_CONFIG_SELECT_STRING_VIEW_STD && !string_HAVE_STD_STRING_VIEW
|
|
# error string-lite: std::string_view selected but is not available: C++17?
|
|
#elif 0 // string_CONFIG_SELECT_STRING_VIEW==string_CONFIG_SELECT_STRING_VIEW_NONSTD && !defined(NONSTD_SV_LITE_H_INCLUDED)
|
|
# error string-lite: string-view-lite selected but is not available: nonstd/string_view.hpp included before nonstd/string.hpp?
|
|
#endif
|
|
|
|
// C++ language version detection (C++23 is speculative):
|
|
// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
|
|
|
|
#ifndef string_CPLUSPLUS
|
|
# if defined(_MSVC_LANG ) && !defined(__clang__)
|
|
# define string_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
|
|
# else
|
|
# define string_CPLUSPLUS __cplusplus
|
|
# endif
|
|
#endif
|
|
|
|
#define string_CPP98_OR_GREATER ( string_CPLUSPLUS >= 199711L )
|
|
#define string_CPP11_OR_GREATER ( string_CPLUSPLUS >= 201103L )
|
|
#define string_CPP14_OR_GREATER ( string_CPLUSPLUS >= 201402L )
|
|
#define string_CPP17_OR_GREATER ( string_CPLUSPLUS >= 201703L )
|
|
#define string_CPP20_OR_GREATER ( string_CPLUSPLUS >= 202002L )
|
|
#define string_CPP23_OR_GREATER ( string_CPLUSPLUS >= 202300L )
|
|
|
|
// MSVC version:
|
|
|
|
#if defined(_MSC_VER ) && !defined(__clang__)
|
|
# define string_COMPILER_MSVC_VER (_MSC_VER )
|
|
# define string_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) )
|
|
# define string_COMPILER_MSVC_VERSION_FULL (_MSC_VER - 100 * ( 5 + (_MSC_VER < 1900 ) ) )
|
|
#else
|
|
# define string_COMPILER_MSVC_VER 0
|
|
# define string_COMPILER_MSVC_VERSION 0
|
|
# define string_COMPILER_MSVC_VERSION_FULL 0
|
|
#endif
|
|
|
|
// clang version:
|
|
|
|
#define string_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) )
|
|
|
|
#if defined( __apple_build_version__ )
|
|
# define string_COMPILER_APPLECLANG_VERSION string_COMPILER_VERSION( __clang_major__, __clang_minor__, __clang_patchlevel__ )
|
|
# define string_COMPILER_CLANG_VERSION 0
|
|
#elif defined( __clang__ )
|
|
# define string_COMPILER_APPLECLANG_VERSION 0
|
|
# define string_COMPILER_CLANG_VERSION string_COMPILER_VERSION( __clang_major__, __clang_minor__, __clang_patchlevel__ )
|
|
#else
|
|
# define string_COMPILER_APPLECLANG_VERSION 0
|
|
# define string_COMPILER_CLANG_VERSION 0
|
|
#endif
|
|
|
|
// GNUC version:
|
|
|
|
#if defined(__GNUC__) && !defined(__clang__)
|
|
# define string_COMPILER_GNUC_VERSION string_COMPILER_VERSION( __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ )
|
|
#else
|
|
# define string_COMPILER_GNUC_VERSION 0
|
|
#endif
|
|
|
|
// half-open range [lo..hi):
|
|
#define string_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
|
|
|
|
// Presence of language and library features:
|
|
|
|
#define string_CPP11_100 (string_CPP11_OR_GREATER || string_COMPILER_MSVC_VER >= 1600)
|
|
#define string_CPP11_110 (string_CPP11_OR_GREATER || string_COMPILER_MSVC_VER >= 1700)
|
|
#define string_CPP11_120 (string_CPP11_OR_GREATER || string_COMPILER_MSVC_VER >= 1800)
|
|
#define string_CPP11_140 (string_CPP11_OR_GREATER || string_COMPILER_MSVC_VER >= 1900)
|
|
#define string_CPP11_141 (string_CPP11_OR_GREATER || string_COMPILER_MSVC_VER >= 1910)
|
|
|
|
#define string_CPP11_000 (string_CPP11_OR_GREATER)
|
|
|
|
#define string_CPP14_000 (string_CPP14_OR_GREATER)
|
|
#define string_CPP14_120 (string_CPP14_OR_GREATER || string_COMPILER_MSVC_VER >= 1800)
|
|
|
|
#define string_CPP17_000 (string_CPP17_OR_GREATER)
|
|
#define string_CPP17_120 (string_CPP17_OR_GREATER || string_COMPILER_MSVC_VER >= 1800)
|
|
#define string_CPP17_140 (string_CPP17_OR_GREATER || string_COMPILER_MSVC_VER >= 1900)
|
|
|
|
#define string_CPP20_000 (string_CPP20_OR_GREATER)
|
|
|
|
// Presence of C++11 language features:
|
|
|
|
#define string_HAVE_FREE_BEGIN string_CPP14_120
|
|
#define string_HAVE_CONSTEXPR_11 (string_CPP11_000 && !string_BETWEEN(string_COMPILER_MSVC_VER, 1, 1910))
|
|
#define string_HAVE_NOEXCEPT string_CPP11_140
|
|
#define string_HAVE_NULLPTR string_CPP11_100
|
|
#define string_HAVE_DEFAULT_FN_TPL_ARGS string_CPP11_120
|
|
#define string_HAVE_EXPLICIT_CONVERSION string_CPP11_120
|
|
#define string_HAVE_CHAR16_T string_CPP11_000
|
|
#define string_HAVE_CHAR32_T string_HAVE_CHAR16_T
|
|
|
|
// Presence of C++14 language features:
|
|
|
|
#define string_HAVE_CONSTEXPR_14 string_CPP14_000
|
|
#define string_HAVE_FREE_BEGIN string_CPP14_120
|
|
|
|
// Presence of C++17 language features:
|
|
|
|
#define string_HAVE_FREE_SIZE string_CPP17_140
|
|
#define string_HAVE_NODISCARD string_CPP17_000
|
|
|
|
// Presence of C++20 language features:
|
|
|
|
#define string_HAVE_CHAR8_T string_CPP20_000
|
|
|
|
// Presence of C++ library features:
|
|
|
|
#define string_HAVE_REGEX (string_CPP11_000 && !string_BETWEEN(string_COMPILER_GNUC_VERSION, 1, 490))
|
|
#define string_HAVE_TYPE_TRAITS string_CPP11_110
|
|
|
|
// Usage of C++ language features:
|
|
|
|
#if string_HAVE_CONSTEXPR_11
|
|
# define string_constexpr constexpr
|
|
#else
|
|
# define string_constexpr /*constexpr*/
|
|
#endif
|
|
|
|
#if string_HAVE_CONSTEXPR_14
|
|
# define string_constexpr14 constexpr
|
|
#else
|
|
# define string_constexpr14 /*constexpr*/
|
|
#endif
|
|
|
|
#if string_HAVE_EXPLICIT_CONVERSION
|
|
# define string_explicit explicit
|
|
#else
|
|
# define string_explicit /*explicit*/
|
|
#endif
|
|
|
|
#if string_HAVE_NOEXCEPT && !string_CONFIG_NO_EXCEPTIONS
|
|
# define string_noexcept noexcept
|
|
#else
|
|
# define string_noexcept /*noexcept*/
|
|
#endif
|
|
|
|
#if string_HAVE_NODISCARD
|
|
# define string_nodiscard [[nodiscard]]
|
|
#else
|
|
# define string_nodiscard /*[[nodiscard]]*/
|
|
#endif
|
|
|
|
#if string_HAVE_NULLPTR
|
|
# define string_nullptr nullptr
|
|
#else
|
|
# define string_nullptr NULL
|
|
#endif
|
|
|
|
#if string_HAVE_EXPLICIT_CONVERSION
|
|
# define string_explicit_cv explicit
|
|
#else
|
|
# define string_explicit_cv /*explicit*/
|
|
#endif
|
|
|
|
#define string_HAS_ENABLE_IF_ (string_HAVE_TYPE_TRAITS && string_HAVE_DEFAULT_FN_TPL_ARGS)
|
|
#define string_TEST_STRING_CONTAINS (string_HAS_ENABLE_IF_ && !string_BETWEEN(string_COMPILER_MSVC_VER, 1, 1910))
|
|
#define string_TEST_STRING_STARTS_WITH string_TEST_STRING_CONTAINS
|
|
#define string_TEST_STRING_ENDS_WITH string_TEST_STRING_CONTAINS
|
|
|
|
// Method enabling (return type):
|
|
|
|
#if string_HAVE_TYPE_TRAITS
|
|
# define string_ENABLE_IF_R_(VA, R) typename std::enable_if< (VA), R >::type
|
|
#else
|
|
# define string_ENABLE_IF_R_(VA, R) R
|
|
#endif
|
|
|
|
// Method enabling (function template argument):
|
|
|
|
#if string_HAS_ENABLE_IF_
|
|
|
|
// VS 2013 seems to have trouble with SFINAE for default non-type arguments:
|
|
# if !string_BETWEEN( string_COMPILER_MSVC_VER, 1, 1900 )
|
|
# define string_ENABLE_IF_(VA) , typename std::enable_if< (VA), int >::type = 0
|
|
# else
|
|
# define string_ENABLE_IF_(VA) , typename = typename std::enable_if< (VA), ::nonstd::string::detail::enabler >::type
|
|
# endif
|
|
|
|
# define string_ENABLE_IF_HAS_METHOD_( type, method) string_ENABLE_IF_( string_HAS_METHOD_( type, method) )
|
|
# define string_DISABLE_IF_HAS_METHOD_( type, method) string_ENABLE_IF_( !string_HAS_METHOD_( type, method) )
|
|
|
|
#else
|
|
# define string_ENABLE_IF_(VA)
|
|
# define string_ENABLE_IF_HAS_METHOD_( type, member)
|
|
# define string_DISABLE_IF_HAS_METHOD_(type, member)
|
|
#endif
|
|
|
|
// Additional includes:
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <cstring>
|
|
#include <iterator>
|
|
#include <locale>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#if string_HAVE_REGEX
|
|
# include <regex>
|
|
#endif
|
|
|
|
#if ! string_CONFIG_NO_EXCEPTIONS
|
|
# include <stdexcept>
|
|
#endif
|
|
|
|
#if string_HAVE_TYPE_TRAITS
|
|
# include <type_traits>
|
|
#elif string_HAVE_TR1_TYPE_TRAITS
|
|
# include <tr1/type_traits>
|
|
#endif
|
|
|
|
#if string_CONFIG_SELECT_STRING_VIEW == string_CONFIG_SELECT_STRING_VIEW_INTERNAL
|
|
// noop
|
|
#elif string_CONFIG_SELECT_STRING_VIEW == string_CONFIG_SELECT_STRING_VIEW_NONSTD
|
|
# define nssv_CONFIG_SELECT_STRING_VIEW nssv_STRING_VIEW_NONSTD
|
|
# include "nonstd/string_view.hpp"
|
|
#elif string_CONFIG_SELECT_STRING_VIEW == string_CONFIG_SELECT_STRING_VIEW_STD
|
|
# include <string_view>
|
|
#endif
|
|
|
|
// Method detection:
|
|
|
|
#define string_HAS_METHOD_( T, M ) \
|
|
::nonstd::string::has_##M<T>::value
|
|
|
|
#if string_CPP11_OR_GREATER && !string_BETWEEN(string_COMPILER_GNUC_VERSION, 1, 500)
|
|
|
|
# define string_MAKE_HAS_METHOD_( M ) \
|
|
template< typename T > \
|
|
using M##_t = decltype(std::declval<T>().M()); \
|
|
\
|
|
template<typename T > \
|
|
using has_##M = std23::is_detected<M##_t, T>;
|
|
|
|
#else // string_CPP11_OR_GREATER
|
|
|
|
# define string_MAKE_HAS_METHOD_( M ) \
|
|
template< typename T > \
|
|
struct has_##M \
|
|
{ \
|
|
typedef char yes[1]; \
|
|
typedef char no[2]; \
|
|
\
|
|
template< typename U > \
|
|
static yes & test( int (*)[sizeof(std98::declval<U>().M(), 1)] ); \
|
|
\
|
|
template< typename U > \
|
|
static no & test(...); \
|
|
\
|
|
static const bool value = sizeof( test<T>(NULL) ) == sizeof(yes); \
|
|
};
|
|
|
|
#endif
|
|
|
|
// Type traits:
|
|
namespace nonstd {
|
|
namespace string {
|
|
|
|
namespace std98 {
|
|
|
|
template< typename T, T v > struct integral_constant { enum { value = v }; };
|
|
typedef integral_constant< bool, true > true_type;
|
|
typedef integral_constant< bool, false > false_type;
|
|
|
|
template< typename T, typename U >
|
|
struct is_same { enum dummy { value = false }; };
|
|
|
|
template< typename T >
|
|
struct is_same<T, T> { enum dummy { value = true }; };
|
|
|
|
template< typename T >
|
|
T declval();
|
|
|
|
} // namespace std98
|
|
|
|
#if string_CPP11_OR_GREATER
|
|
|
|
namespace std11 {
|
|
|
|
template< bool B, typename T, typename F >
|
|
struct conditional { typedef T type; };
|
|
|
|
template< typename T, typename F >
|
|
struct conditional<false, T, F> { typedef F type; };
|
|
|
|
template< typename T > struct remove_const { typedef T type; };
|
|
template< typename T > struct remove_const<const T> { typedef T type; };
|
|
|
|
template< typename T > struct remove_volatile { typedef T type; };
|
|
template< typename T > struct remove_volatile<volatile T> { typedef T type; };
|
|
|
|
template< typename T > struct remove_cv { typedef typename remove_volatile<typename remove_const<T>::type>::type type; };
|
|
|
|
// template< typename T > struct is_const : std98::false_type {};
|
|
// template< typename T > struct is_const<const T> : std98::true_type {};
|
|
|
|
template< typename T > struct is_pointer_helper : std98::false_type {};
|
|
template< typename T > struct is_pointer_helper<T*> : std98::true_type {};
|
|
template< typename T > struct is_pointer : is_pointer_helper<typename remove_cv<T>::type> {};
|
|
|
|
} // C++11
|
|
|
|
namespace std14 {
|
|
|
|
template< bool B, typename T, typename F >
|
|
using conditional_t = typename std11::conditional<B,T,F>::type;
|
|
|
|
} // namespace c++14
|
|
|
|
namespace std17 {
|
|
|
|
template< typename... >
|
|
using void_t = void;
|
|
|
|
} // namespace c++17
|
|
|
|
namespace std20 {
|
|
|
|
// type identity, to establish non-deduced contexts in template argument deduction:
|
|
|
|
template< typename T >
|
|
struct type_identity
|
|
{
|
|
typedef T type;
|
|
};
|
|
|
|
#if string_CPP11_100
|
|
|
|
struct identity
|
|
{
|
|
template< typename T >
|
|
string_constexpr T && operator ()( T && arg ) const string_noexcept
|
|
{
|
|
return std::forward<T>( arg );
|
|
}
|
|
};
|
|
|
|
#endif // string_CPP11_100
|
|
|
|
} // namespace c++20
|
|
|
|
namespace std23 {
|
|
namespace detail {
|
|
|
|
template< typename Default, typename AlwaysVoid, template<class...> class Op, typename... Args >
|
|
struct detector
|
|
{
|
|
using value_t = std::false_type;
|
|
using type = Default;
|
|
};
|
|
|
|
template< typename Default, template<class...> class Op, typename... Args >
|
|
struct detector<Default, std17::void_t<Op<Args...>>, Op, Args...>
|
|
{
|
|
using value_t = std::true_type;
|
|
using type = Op<Args...>;
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
struct nonesuch
|
|
{
|
|
~nonesuch() = delete;
|
|
nonesuch( nonesuch const & ) = delete;
|
|
void operator=( nonesuch const & ) = delete;
|
|
};
|
|
|
|
// pre-C+17 requires `class Op`:
|
|
template< template<typename...> class Op, typename... Args >
|
|
using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t;
|
|
|
|
} // std23
|
|
|
|
#endif // string_CPP11_OR_GREATER
|
|
|
|
// for string_HAS_METHOD_:
|
|
|
|
string_MAKE_HAS_METHOD_( begin )
|
|
string_MAKE_HAS_METHOD_( clear )
|
|
string_MAKE_HAS_METHOD_( contains )
|
|
string_MAKE_HAS_METHOD_( empty )
|
|
string_MAKE_HAS_METHOD_( starts_with )
|
|
string_MAKE_HAS_METHOD_( ends_with )
|
|
string_MAKE_HAS_METHOD_( replace )
|
|
|
|
// string-lite API functions:
|
|
|
|
// Utilities:
|
|
|
|
template< typename CharT >
|
|
string_nodiscard CharT nullchr() string_noexcept
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// free function min():
|
|
|
|
template< typename T >
|
|
inline T min( T a, typename std20::type_identity<T>::type b )
|
|
{
|
|
return a < b ? a : b;
|
|
}
|
|
|
|
// free function length():
|
|
|
|
template< typename Coll >
|
|
string_nodiscard size_t length( Coll const & coll )
|
|
{
|
|
return coll.length();
|
|
}
|
|
|
|
#if string_HAVE_FREE_SIZE
|
|
|
|
using std::size;
|
|
|
|
#else // string_HAVE_FREE_SIZE
|
|
|
|
template< typename Cont >
|
|
string_nodiscard inline size_t size( Cont const & c )
|
|
{
|
|
return c.size();
|
|
}
|
|
|
|
#endif // string_HAVE_FREE_SIZE
|
|
|
|
// nonstd size(C-string)
|
|
|
|
// TODO Add char16_t, char32_t, wchar_t variations - size()
|
|
|
|
string_nodiscard inline size_t size( char * s )
|
|
{
|
|
return strlen( s );
|
|
}
|
|
|
|
string_nodiscard inline size_t size( char const * s )
|
|
{
|
|
return strlen( s );
|
|
}
|
|
|
|
string_nodiscard inline size_t size( wchar_t * s )
|
|
{
|
|
return wcslen( s );
|
|
}
|
|
|
|
string_nodiscard inline size_t size( wchar_t const * s )
|
|
{
|
|
return wcslen( s );
|
|
}
|
|
|
|
#if string_HAVE_CHAR16_T
|
|
#endif
|
|
|
|
// non-standard begin(), end() for char*:
|
|
|
|
// TODO Add char16_t, char32_t, wchar_t variations - begin(), end()
|
|
|
|
template< typename PCharT >
|
|
string_nodiscard inline
|
|
string_ENABLE_IF_R_(std11::is_pointer<PCharT>::value, PCharT)
|
|
begin( PCharT text )
|
|
{
|
|
return text;
|
|
}
|
|
|
|
template< typename PCharT >
|
|
string_nodiscard inline
|
|
string_ENABLE_IF_R_(std11::is_pointer<PCharT>::value, PCharT)
|
|
end( PCharT text )
|
|
{
|
|
return text + size( text );
|
|
}
|
|
|
|
template< typename CharT >
|
|
string_nodiscard inline CharT const *
|
|
cbegin( CharT const * text )
|
|
{
|
|
return text;
|
|
}
|
|
|
|
template< typename CharT >
|
|
string_nodiscard inline CharT const *
|
|
cend( CharT const * text )
|
|
{
|
|
return text + size( text );
|
|
}
|
|
|
|
// standard begin() and end():
|
|
|
|
#if string_HAVE_FREE_BEGIN
|
|
|
|
using std::begin;
|
|
using std::cbegin;
|
|
using std::crbegin;
|
|
using std::end;
|
|
using std::cend;
|
|
using std::crend;
|
|
|
|
#else // string_HAVE_FREE_BEGIN
|
|
|
|
template< typename StringT >
|
|
string_nodiscard inline typename StringT::iterator begin( StringT & text )
|
|
{
|
|
return text.begin();
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_nodiscard inline typename StringT::iterator end( StringT & text )
|
|
{
|
|
return text.end();
|
|
}
|
|
|
|
#if string_CPP11_000
|
|
|
|
template< typename StringT >
|
|
string_nodiscard inline typename StringT::const_iterator begin( StringT const & text )
|
|
{
|
|
return text.cbegin();
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_nodiscard inline typename StringT::const_iterator end( StringT const & text )
|
|
{
|
|
return text.cend();
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_nodiscard inline typename StringT::const_iterator cbegin( StringT const & text )
|
|
{
|
|
return text.cbegin();
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_nodiscard inline typename StringT::const_iterator cend( StringT const & text )
|
|
{
|
|
return text.cend();
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_nodiscard inline typename StringT::reverse_iterator rbegin( StringT const & text )
|
|
{
|
|
return text.rbegin();
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_nodiscard inline typename StringT::reverse_iterator rend( StringT const & text )
|
|
{
|
|
return text.rend();
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_nodiscard inline typename StringT::const_reverse_iterator crbegin( StringT const & text )
|
|
{
|
|
return text.crbegin();
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_nodiscard inline typename StringT::const_reverse_iterator crend( StringT const & text )
|
|
{
|
|
return text.crend();
|
|
}
|
|
|
|
#else // string_CPP11_000
|
|
|
|
template< typename StringT >
|
|
string_nodiscard inline typename StringT::const_iterator cbegin( StringT const & text )
|
|
{
|
|
typedef typename StringT::const_iterator const_iterator;
|
|
return const_iterator( text.begin() );
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_nodiscard inline typename StringT::const_iterator cend( StringT const & text )
|
|
{
|
|
typedef typename StringT::const_iterator const_iterator;
|
|
return const_iterator( text.end() );
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_nodiscard inline typename StringT::const_reverse_iterator crbegin( StringT const & text )
|
|
{
|
|
typedef typename StringT::const_reverse_iterator const_reverse_iterator;
|
|
return const_reverse_iterator( text.rbegin() );
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_nodiscard inline typename StringT::const_reverse_iterator crend( StringT const & text )
|
|
{
|
|
typedef typename StringT::const_reverse_iterator const_reverse_iterator;
|
|
return const_reverse_iterator( text.rend() );
|
|
}
|
|
|
|
#endif // string_CPP11_000
|
|
#endif // string_HAVE_FREE_BEGIN
|
|
|
|
// Minimal internal string_view for string algorithm library, when requested:
|
|
|
|
#if string_CONFIG_SELECT_STRING_VIEW == string_CONFIG_SELECT_STRING_VIEW_INTERNAL
|
|
|
|
template
|
|
<
|
|
class CharT,
|
|
class Traits = std::char_traits<CharT>
|
|
>
|
|
class basic_string_view
|
|
{
|
|
public:
|
|
// Member types:
|
|
|
|
typedef Traits traits_type;
|
|
typedef CharT value_type;
|
|
|
|
typedef CharT * pointer;
|
|
typedef CharT const * const_pointer;
|
|
typedef CharT & reference;
|
|
typedef CharT const & const_reference;
|
|
|
|
typedef const_pointer iterator;
|
|
typedef const_pointer const_iterator;
|
|
typedef std::reverse_iterator< const_iterator > reverse_iterator;
|
|
typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
|
|
|
|
typedef std::size_t size_type;
|
|
typedef std::ptrdiff_t difference_type;
|
|
|
|
// 24.4.2.1 Construction and assignment:
|
|
|
|
string_constexpr basic_string_view() string_noexcept
|
|
: data_( string_nullptr )
|
|
, size_( 0 )
|
|
{}
|
|
|
|
string_constexpr basic_string_view( CharT const * s ) string_noexcept // non-standard noexcept
|
|
: data_( s )
|
|
, size_( Traits::length(s) )
|
|
{}
|
|
|
|
string_constexpr basic_string_view( CharT const * s, size_type count ) string_noexcept // non-standard noexcept
|
|
: data_( s )
|
|
, size_( count )
|
|
{}
|
|
|
|
string_constexpr basic_string_view( CharT const * b, CharT const * e ) string_noexcept // non-standard noexcept
|
|
: data_( b )
|
|
, size_( e - b )
|
|
{}
|
|
|
|
string_constexpr basic_string_view( std::basic_string<CharT> & s ) string_noexcept // non-standard noexcept
|
|
: data_( s.data() )
|
|
, size_( s.size() )
|
|
{}
|
|
|
|
string_constexpr basic_string_view( std::basic_string<CharT> const & s ) string_noexcept // non-standard noexcept
|
|
: data_( s.data() )
|
|
, size_( s.size() )
|
|
{}
|
|
|
|
// #if string_HAVE_EXPLICIT_CONVERSION
|
|
|
|
template< class Allocator >
|
|
string_explicit operator std::basic_string<CharT, Traits, Allocator>() const
|
|
{
|
|
return to_string( Allocator() );
|
|
}
|
|
|
|
// #endif // string_HAVE_EXPLICIT_CONVERSION
|
|
|
|
#if string_CPP11_OR_GREATER
|
|
|
|
template< class Allocator = std::allocator<CharT> >
|
|
std::basic_string<CharT, Traits, Allocator>
|
|
to_string( Allocator const & a = Allocator() ) const
|
|
{
|
|
return std::basic_string<CharT, Traits, Allocator>( begin(), end(), a );
|
|
}
|
|
|
|
#else
|
|
|
|
std::basic_string<CharT, Traits>
|
|
to_string() const
|
|
{
|
|
return std::basic_string<CharT, Traits>( begin(), end() );
|
|
}
|
|
|
|
template< class Allocator >
|
|
std::basic_string<CharT, Traits, Allocator>
|
|
to_string( Allocator const & a ) const
|
|
{
|
|
return std::basic_string<CharT, Traits, Allocator>( begin(), end(), a );
|
|
}
|
|
|
|
#endif // string_CPP11_OR_GREATER
|
|
|
|
|
|
string_constexpr14 size_type find( basic_string_view v, size_type pos = 0 ) const string_noexcept // (1)
|
|
{
|
|
return assert( v.size() == 0 || v.data() != string_nullptr )
|
|
, pos >= size()
|
|
? npos
|
|
: to_pos( std::search( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) );
|
|
}
|
|
|
|
string_constexpr14 size_type find( CharT c, size_type pos = 0 ) const string_noexcept // (2)
|
|
{
|
|
return find( basic_string_view( &c, 1 ), pos );
|
|
}
|
|
|
|
string_constexpr size_type find_first_of( basic_string_view v, size_type pos = 0 ) const string_noexcept // (1)
|
|
{
|
|
return pos >= size()
|
|
? npos
|
|
: to_pos( std::find_first_of( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) );
|
|
}
|
|
|
|
string_constexpr size_type size() const string_noexcept { return size_; }
|
|
string_constexpr size_type length() const string_noexcept { return size_; }
|
|
string_constexpr const_pointer data() const string_noexcept { return data_; }
|
|
|
|
string_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const
|
|
{
|
|
#if string_CONFIG_NO_EXCEPTIONS
|
|
assert( pos <= size() );
|
|
#else
|
|
if ( pos > size() )
|
|
{
|
|
throw std::out_of_range("string_view::substr()");
|
|
}
|
|
#endif
|
|
return basic_string_view( data() + pos, (std::min)( n, size() - pos ) );
|
|
}
|
|
|
|
string_constexpr const_iterator begin() const string_noexcept { return data_; }
|
|
string_constexpr const_iterator end() const string_noexcept { return data_ + size_; }
|
|
|
|
string_constexpr const_iterator cbegin() const string_noexcept { return begin(); }
|
|
string_constexpr const_iterator cend() const string_noexcept { return end(); }
|
|
|
|
string_constexpr const_reverse_iterator rbegin() const string_noexcept { return const_reverse_iterator( end() ); }
|
|
string_constexpr const_reverse_iterator rend() const string_noexcept { return const_reverse_iterator( begin() ); }
|
|
|
|
string_constexpr const_reverse_iterator crbegin() const string_noexcept { return rbegin(); }
|
|
string_constexpr const_reverse_iterator crend() const string_noexcept { return rend(); }
|
|
|
|
string_constexpr size_type to_pos( const_iterator it ) const
|
|
{
|
|
return it == cend() ? npos : size_type( it - cbegin() );
|
|
}
|
|
|
|
// Constants:
|
|
|
|
#if string_CPP17_OR_GREATER
|
|
static string_constexpr size_type npos = size_type(-1);
|
|
#elif string_CPP11_OR_GREATER
|
|
enum : size_type { npos = size_type(-1) };
|
|
#else
|
|
enum { npos = size_type(-1) };
|
|
#endif
|
|
|
|
private:
|
|
const_pointer data_;
|
|
size_type size_;
|
|
};
|
|
|
|
typedef basic_string_view<char> string_view;
|
|
typedef basic_string_view<wchar_t> wstring_view;
|
|
|
|
#if string_HAVE_CHAR16_T
|
|
|
|
typedef basic_string_view<char16_t> u16string_view;
|
|
typedef basic_string_view<char32_t> u32string_view;
|
|
#endif
|
|
|
|
template< typename T >
|
|
string_nodiscard inline size_t size( basic_string_view<T> const & sv )
|
|
{
|
|
return sv.size();
|
|
}
|
|
|
|
template< typename T >
|
|
string_nodiscard inline typename basic_string_view<T>::const_reverse_iterator
|
|
crbegin( basic_string_view<T> const & sv )
|
|
{
|
|
typedef typename basic_string_view<T>::const_reverse_iterator const_reverse_iterator;
|
|
return const_reverse_iterator( sv.rbegin() );
|
|
}
|
|
|
|
template< typename T >
|
|
string_nodiscard inline typename basic_string_view<T>::const_reverse_iterator
|
|
crend( basic_string_view<T> const & sv )
|
|
{
|
|
typedef typename basic_string_view<T>::const_reverse_iterator const_reverse_iterator;
|
|
return const_reverse_iterator( sv.rend() );
|
|
}
|
|
|
|
#elif string_CONFIG_SELECT_STRING_VIEW == string_CONFIG_SELECT_STRING_VIEW_NONSTD
|
|
|
|
using nonstd::sv_lite::basic_string_view;
|
|
using nonstd::sv_lite::string_view;
|
|
using nonstd::sv_lite::wstring_view;
|
|
|
|
#if nssv_HAVE_WCHAR16_T
|
|
using nonstd::sv_lite::u16string_view;
|
|
#endif
|
|
#if nssv_HAVE_WCHAR32_T
|
|
using nonstd::sv_lite::u32string_view;
|
|
#endif
|
|
|
|
#elif string_CONFIG_SELECT_STRING_VIEW == string_CONFIG_SELECT_STRING_VIEW_STD
|
|
|
|
using std::basic_string_view;
|
|
using std::string_view;
|
|
using std::wstring_view;
|
|
#if string_HAVE_CHAR16_T
|
|
using std::u16string_view;
|
|
using std::u32string_view;
|
|
#endif
|
|
|
|
#endif // string_CONFIG_SELECT_STRING_VIEW_INTERNAL
|
|
|
|
// Convert string_view to std::string:
|
|
|
|
#if string_CONFIG_SELECT_STRING_VIEW != string_CONFIG_SELECT_STRING_VIEW_NONSTD
|
|
|
|
template< class CharT, class Traits >
|
|
std::basic_string<CharT, Traits>
|
|
to_string( basic_string_view<CharT, Traits> v )
|
|
{
|
|
return std::basic_string<CharT, Traits>( v.begin(), v.end() );
|
|
}
|
|
|
|
template< class CharT, class Traits, class Allocator >
|
|
std::basic_string<CharT, Traits, Allocator>
|
|
to_string( basic_string_view<CharT, Traits> v, Allocator const & a )
|
|
{
|
|
return std::basic_string<CharT, Traits, Allocator>( v.begin(), v.end(), a );
|
|
}
|
|
|
|
#endif // string_CONFIG_SELECT_STRING_VIEW
|
|
|
|
// to_identity() - let nonstd::string_view operate with pre-std::string_view std::string methods:
|
|
|
|
template< class CharT >
|
|
CharT const * to_identity( CharT const * s )
|
|
{
|
|
return s;
|
|
}
|
|
|
|
template< class CharT, class Traits, class Allocator >
|
|
std::basic_string<CharT, Traits, Allocator>
|
|
to_identity( std::basic_string<CharT, Traits, Allocator> const & s )
|
|
{
|
|
return s;
|
|
}
|
|
|
|
#if string_CONFIG_SELECT_STRING_VIEW == string_CONFIG_SELECT_STRING_VIEW_STD
|
|
|
|
template< class CharT, class Traits >
|
|
basic_string_view<CharT, Traits>
|
|
to_identity( basic_string_view<CharT, Traits> v )
|
|
{
|
|
return v;
|
|
}
|
|
|
|
#else
|
|
|
|
template< class CharT, class Traits >
|
|
std::basic_string<CharT, Traits>
|
|
to_identity( basic_string_view<CharT, Traits> v )
|
|
{
|
|
return to_string( v );
|
|
}
|
|
|
|
#endif // string_CONFIG_SELECT_STRING_VIEW
|
|
|
|
// namespace detail:
|
|
|
|
namespace detail {
|
|
|
|
// for string_ENABLE_IF_():
|
|
|
|
/*enum*/ class enabler{};
|
|
|
|
template< typename CharT >
|
|
string_nodiscard inline CharT as_lowercase( CharT chr )
|
|
{
|
|
return std::tolower( chr, std::locale() );
|
|
}
|
|
|
|
template< typename CharT >
|
|
string_nodiscard inline CharT as_uppercase( CharT chr )
|
|
{
|
|
return std::toupper( chr, std::locale() );
|
|
}
|
|
|
|
// case conversion:
|
|
|
|
// Note: serve both CharT* and StringT&:
|
|
|
|
template< typename StringT, typename Fn >
|
|
StringT & to_case( StringT & text, Fn fn ) string_noexcept
|
|
{
|
|
std::transform(
|
|
string::begin( text ), string::end( text )
|
|
, string::begin( text )
|
|
, fn
|
|
);
|
|
|
|
return text;
|
|
}
|
|
|
|
// find_first():
|
|
|
|
template< typename StringT, typename SubT >
|
|
typename StringT::iterator find_first( StringT & text, SubT const & seek )
|
|
{
|
|
return std::search(
|
|
string::begin(text), string::end(text)
|
|
, string::cbegin(seek), string::cend(seek)
|
|
);
|
|
}
|
|
|
|
template< typename StringT, typename SubT >
|
|
typename StringT::const_iterator find_first( StringT const & text, SubT const & seek )
|
|
{
|
|
return std::search(
|
|
string::cbegin(text), string::cend(text)
|
|
, string::cbegin(seek), string::cend(seek)
|
|
);
|
|
}
|
|
|
|
// find_last():
|
|
|
|
template< typename StringIt, typename SubIt, typename PredicateT >
|
|
StringIt find_last( StringIt text_pos, StringIt text_end, SubIt seek_pos, SubIt seek_end, PredicateT compare )
|
|
{
|
|
if ( seek_pos == seek_end )
|
|
return text_end;
|
|
|
|
StringIt result = text_end;
|
|
|
|
while ( true )
|
|
{
|
|
StringIt new_result = std::search( text_pos, text_end, seek_pos, seek_end, compare );
|
|
|
|
if ( new_result == text_end )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
result = new_result;
|
|
text_pos = result;
|
|
++text_pos;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template< typename StringT, typename SubT, typename PredicateT >
|
|
typename StringT::iterator find_last( StringT & text, SubT const & seek, PredicateT compare )
|
|
{
|
|
return detail::find_last(
|
|
string::begin(text), string::end(text)
|
|
, string::cbegin(seek), string::cend(seek)
|
|
, compare
|
|
);
|
|
}
|
|
|
|
template< typename StringT, typename SubT, typename PredicateT >
|
|
typename StringT::const_iterator find_last( StringT const & text, SubT const & seek, PredicateT compare )
|
|
{
|
|
return detail::find_last(
|
|
string::cbegin(text), string::cend(text)
|
|
, string::cbegin(seek), string::cend(seek)
|
|
, compare
|
|
);
|
|
}
|
|
|
|
// starts_with():
|
|
|
|
template< typename StringIt, typename SubIt, typename PredicateT >
|
|
bool starts_with( StringIt text_pos, StringIt text_end, SubIt seek_pos, SubIt seek_end, PredicateT compare )
|
|
{
|
|
for( ; text_pos != text_end && seek_pos != seek_end; ++text_pos, ++seek_pos )
|
|
{
|
|
if( !compare( *text_pos, *seek_pos ) )
|
|
return false;
|
|
}
|
|
|
|
return seek_pos == seek_end;
|
|
}
|
|
|
|
template< typename StringT, typename SubT, typename PredicateT >
|
|
bool starts_with( StringT const & text, SubT const & seek, PredicateT compare )
|
|
{
|
|
return detail::starts_with(
|
|
string::cbegin(text), string::cend(text)
|
|
, string::cbegin(seek), string::cend(seek)
|
|
, compare
|
|
);
|
|
}
|
|
|
|
// ends_with():
|
|
|
|
template< typename StringT, typename SubT, typename PredicateT >
|
|
bool ends_with( StringT const & text, SubT const & seek, PredicateT compare )
|
|
{
|
|
return detail::starts_with(
|
|
string::crbegin(text), string::crend(text)
|
|
, string::crbegin(seek), string::crend(seek)
|
|
, compare
|
|
);
|
|
}
|
|
|
|
// replace_all():
|
|
|
|
// TODO replace_all() - alg:
|
|
|
|
template< typename StringIt, typename FromIt, typename ToIt, typename PredicateT >
|
|
bool replace_all
|
|
(
|
|
StringIt text_pos, StringIt text_end
|
|
, FromIt from_pos, FromIt from_end
|
|
, ToIt to_pos, ToIt to_end
|
|
, PredicateT compare
|
|
)
|
|
{
|
|
return true; // error
|
|
}
|
|
|
|
template< typename CharT, typename FromT, typename ToT >
|
|
std::basic_string<CharT> & replace_all( std::basic_string<CharT> & text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
for ( ;; )
|
|
{
|
|
const size_t pos = text.find( from );
|
|
|
|
if ( pos == std::string::npos )
|
|
return text;
|
|
|
|
text.replace( pos, ::nonstd::string::size(from), to_identity(to) );
|
|
}
|
|
}
|
|
|
|
template< typename StringT, typename FromT, typename ToT >
|
|
StringT & replace_all( StringT & text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
typedef typename StringT::value_type A;
|
|
|
|
(void) detail::replace_all(
|
|
string::begin(text), string::end(text)
|
|
, string::cbegin(from), string::cend(from)
|
|
, string::cbegin(to), string::cend(to)
|
|
, std::equal_to<A>()
|
|
);
|
|
|
|
return text;
|
|
}
|
|
|
|
// replace_first():
|
|
|
|
template< typename CharT, typename FromT, typename ToT >
|
|
std::basic_string<CharT> & replace_first( std::basic_string<CharT> & text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
const size_t pos = text.find( to_identity(from) );
|
|
|
|
if ( pos == std::string::npos )
|
|
return text;
|
|
|
|
return text.replace( pos, ::nonstd::string::size(from), to_identity(to) );
|
|
}
|
|
|
|
// replace_last():
|
|
|
|
template< typename CharT, typename FromT, typename ToT >
|
|
std::basic_string<CharT> &
|
|
replace_last( std::basic_string<CharT> & text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
const size_t pos = text.rfind( to_identity(from) );
|
|
|
|
if ( pos == std::string::npos )
|
|
return text;
|
|
|
|
return text.replace( pos, ::nonstd::string::size(from), to_identity(to) );
|
|
}
|
|
|
|
// append():
|
|
|
|
template< typename CharT, typename TailT >
|
|
string_constexpr CharT *
|
|
append( CharT * text, TailT const & tail ) string_noexcept
|
|
{
|
|
return std::strcat( text, to_identity(tail) );
|
|
}
|
|
|
|
template< typename CharT, typename TailT >
|
|
string_constexpr std::basic_string<CharT> &
|
|
append( std::basic_string<CharT> & text, TailT const & tail ) string_noexcept
|
|
{
|
|
return text.append( to_identity(tail) );
|
|
}
|
|
|
|
// TODO trim() - alg
|
|
|
|
template< typename CharT, typename SetT >
|
|
string_constexpr14 CharT *
|
|
trim_left( CharT * text, SetT const * set ) string_noexcept
|
|
{
|
|
// TODO trim() - strspn(CharT), adapt std::strspn to CharT
|
|
const int pos = std::strspn( text, set );
|
|
|
|
memmove( text, text + pos, 1 + size( text ) - pos );
|
|
|
|
return text;
|
|
}
|
|
|
|
template< typename CharT, typename SetT >
|
|
string_constexpr std::basic_string<CharT> &
|
|
trim_left( std::basic_string<CharT> & text, SetT const & set ) string_noexcept
|
|
{
|
|
return text.erase( 0, text.find_first_not_of( set ) );
|
|
}
|
|
|
|
template< typename StringT, typename SetT >
|
|
string_constexpr StringT &
|
|
trim_left( StringT & text, SetT const & /*set*/ ) string_noexcept
|
|
{
|
|
// TODO trim() - make generic version
|
|
return text;
|
|
}
|
|
|
|
template< typename CharT, typename SetT >
|
|
string_constexpr14 CharT *
|
|
trim_right( CharT * text, SetT const * set ) string_noexcept
|
|
{
|
|
std::size_t length = size( text );
|
|
|
|
if ( !length )
|
|
return text;
|
|
|
|
char * end = text + length - 1;
|
|
|
|
// TODO trim() - strspn(CharT), adapt std::strchr to CharT
|
|
while ( end >= text && std::strchr( set, *end ) )
|
|
end--;
|
|
|
|
*( end + 1 ) = '\0';
|
|
|
|
return text;
|
|
}
|
|
|
|
template< typename CharT, typename SetT >
|
|
string_constexpr std::basic_string<CharT> &
|
|
trim_right( std::basic_string<CharT> & text, SetT const & set ) string_noexcept
|
|
{
|
|
return text.erase( text.find_last_not_of( set ) + 1 );
|
|
}
|
|
|
|
template< typename StringT, typename SetT >
|
|
string_constexpr StringT &
|
|
trim_right( StringT & text, SetT const & /*set*/ ) string_noexcept
|
|
{
|
|
// TODO trim() - make generic version
|
|
return text;
|
|
}
|
|
|
|
template< typename CharT, typename SetT >
|
|
string_constexpr CharT *
|
|
trim( CharT * text, SetT const * set ) string_noexcept
|
|
{
|
|
return trim_right( trim_left( text, set ), set);
|
|
}
|
|
|
|
template< typename CharT, typename SetT >
|
|
string_constexpr std::basic_string<CharT> &
|
|
trim( std::basic_string<CharT> & text, SetT const & set ) string_noexcept
|
|
{
|
|
return trim_right( trim_left( text, set ), set);
|
|
}
|
|
|
|
template< typename StringT, typename SetT >
|
|
string_constexpr StringT &
|
|
trim( StringT & text, SetT const & set ) string_noexcept
|
|
{
|
|
return trim_right( trim_left( text, set ), set);
|
|
}
|
|
|
|
// TODO join() - alg
|
|
|
|
// split():
|
|
|
|
template< typename CharT, typename Delimiter >
|
|
std::vector<basic_string_view<CharT> >
|
|
split(basic_string_view<CharT> text, Delimiter delimiter)
|
|
{
|
|
std::vector<basic_string_view<CharT> > result;
|
|
|
|
size_t pos = 0;
|
|
|
|
for( basic_string_view<CharT> sv = delimiter(text, pos); sv.cbegin() != text.cend(); sv = delimiter(text, pos) )
|
|
{
|
|
result.push_back(sv);
|
|
pos = (sv.end() - text.begin()) + length(delimiter);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
} // namespace detail
|
|
|
|
// Observers:
|
|
|
|
// is_empty():
|
|
|
|
template< typename CharT >
|
|
string_nodiscard bool is_empty( CharT const * cp ) string_noexcept
|
|
{
|
|
assert( cp != string_nullptr );
|
|
return *cp == nullchr<CharT>();
|
|
}
|
|
|
|
template< typename StringT
|
|
string_ENABLE_IF_HAS_METHOD_(StringT, empty)
|
|
>
|
|
string_nodiscard bool is_empty( StringT const & text ) string_noexcept
|
|
{
|
|
return text.empty();
|
|
}
|
|
|
|
// find_first():
|
|
|
|
template< typename StringT, typename SubT
|
|
string_ENABLE_IF_HAS_METHOD_(StringT, begin)
|
|
>
|
|
string_constexpr typename StringT::iterator find_first( StringT & text, SubT const & seek )
|
|
{
|
|
return detail::find_first( text, seek );
|
|
}
|
|
|
|
template< typename StringT, typename SubT
|
|
string_ENABLE_IF_HAS_METHOD_(StringT, begin)
|
|
>
|
|
string_constexpr typename StringT::const_iterator find_first( StringT const & text, SubT const & seek )
|
|
{
|
|
return detail::find_first( text, seek );
|
|
}
|
|
|
|
template< typename CharT, typename SubT >
|
|
string_constexpr CharT * find_first( CharT * text, SubT const & seek )
|
|
{
|
|
return detail::find_first( text, seek );
|
|
}
|
|
|
|
// find_last():
|
|
|
|
template< typename StringT, typename SubT
|
|
string_ENABLE_IF_HAS_METHOD_(StringT, begin)
|
|
>
|
|
string_constexpr typename StringT::iterator find_last( StringT & text, SubT const & seek )
|
|
{
|
|
typedef typename StringT::value_type A;
|
|
|
|
return detail::find_last( text, seek, std::equal_to<A>() );
|
|
}
|
|
|
|
template< typename StringT, typename SubT
|
|
string_ENABLE_IF_HAS_METHOD_(StringT, begin)
|
|
>
|
|
string_constexpr typename StringT::const_iterator find_last( StringT const & text, SubT const & seek )
|
|
{
|
|
typedef typename StringT::value_type A;
|
|
|
|
return detail::find_last( text, seek, std::equal_to<A>() );
|
|
}
|
|
|
|
template< typename CharT, typename SubT >
|
|
string_constexpr CharT * find_last( CharT * text, SubT const & seek )
|
|
{
|
|
return detail::find_last( text, seek, std::equal_to<CharT>() );
|
|
}
|
|
|
|
// contains(); C++23-like string::contains():
|
|
|
|
#if string_TEST_STRING_CONTAINS
|
|
|
|
template< typename StringT, typename SubT
|
|
string_ENABLE_IF_HAS_METHOD_(StringT, contains)
|
|
>
|
|
string_nodiscard string_constexpr bool contains( StringT const & text, SubT const & seek ) string_noexcept
|
|
{
|
|
return text.contains( seek );
|
|
}
|
|
|
|
template< typename StringT, typename SubT
|
|
string_DISABLE_IF_HAS_METHOD_(StringT, contains)
|
|
string_ENABLE_IF_( !std::is_arithmetic<SubT>::value )
|
|
>
|
|
string_nodiscard string_constexpr bool contains( StringT const & text, SubT const & seek ) string_noexcept
|
|
{
|
|
return string::end( text ) != find_first( text, seek );
|
|
}
|
|
|
|
template< typename StringT, typename CharT
|
|
string_DISABLE_IF_HAS_METHOD_(StringT, contains)
|
|
string_ENABLE_IF_( std::is_arithmetic<CharT>::value )
|
|
>
|
|
string_nodiscard string_constexpr14 bool contains( StringT const & text, CharT seek ) string_noexcept
|
|
{
|
|
CharT look[] = { seek, nullchr<CharT>() };
|
|
return string::end( text ) != find_first( text, look );
|
|
}
|
|
|
|
#else // string_TEST_STRING_CONTAINS
|
|
|
|
template< typename StringT, typename SubT >
|
|
string_nodiscard string_constexpr bool contains( StringT const & text, SubT const & seek ) string_noexcept
|
|
{
|
|
return string::cend( text ) != find_first( text, seek );
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_nodiscard string_constexpr bool contains( StringT const & text, char const * seek ) string_noexcept
|
|
{
|
|
return string::cend( text ) != find_first( text, seek );
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_nodiscard string_constexpr bool contains( StringT const & text, char seek ) string_noexcept
|
|
{
|
|
char look[] = { seek, nullchr<char>() };
|
|
return string::cend( text ) != find_first( text, look );
|
|
}
|
|
|
|
#endif // string_TEST_STRING_CONTAINS
|
|
|
|
#if string_HAVE_REGEX
|
|
|
|
template< typename StringT >
|
|
string_nodiscard string_constexpr bool contains( StringT const & text, std::regex const & re ) string_noexcept
|
|
{
|
|
return std::regex_search( text, re );
|
|
}
|
|
|
|
template< typename StringT, typename ReT >
|
|
string_nodiscard string_constexpr bool contains_re( StringT const & text, ReT const & re ) string_noexcept
|
|
{
|
|
return contains( text, std::regex(re) );
|
|
}
|
|
|
|
#endif // string_HAVE_REGEX
|
|
|
|
// starts_with():
|
|
|
|
#if string_TEST_STRING_STARTS_WITH
|
|
|
|
template< typename StringT, typename SubT
|
|
string_ENABLE_IF_HAS_METHOD_(StringT, starts_with)
|
|
>
|
|
string_nodiscard string_constexpr bool starts_with( StringT const & text, SubT const & seek ) string_noexcept
|
|
{
|
|
return text.starts_with( seek );
|
|
}
|
|
|
|
template< typename StringT, typename SubT
|
|
string_DISABLE_IF_HAS_METHOD_(StringT, starts_with)
|
|
string_ENABLE_IF_( !std::is_arithmetic<SubT>::value )
|
|
>
|
|
string_nodiscard string_constexpr bool starts_with( StringT const & text, SubT const & seek ) string_noexcept
|
|
{
|
|
typedef typename StringT::value_type A;
|
|
|
|
return detail::starts_with( text, seek, std::equal_to<A>() );
|
|
}
|
|
|
|
template< typename StringT, typename CharT
|
|
string_DISABLE_IF_HAS_METHOD_(StringT, starts_with)
|
|
string_ENABLE_IF_( std::is_arithmetic<CharT>::value )
|
|
>
|
|
string_nodiscard string_constexpr14 bool starts_with( StringT const & text, CharT seek ) string_noexcept
|
|
{
|
|
CharT look[] = { seek, nullchr<CharT>() };
|
|
|
|
// return detail::starts_with( text, look, std::equal_to<CharT>() );
|
|
return detail::starts_with( text, StringT(look), std::equal_to<CharT>() );
|
|
}
|
|
|
|
#else // string_TEST_STRING_STARTS_WITH
|
|
|
|
template< typename StringT, typename SubT >
|
|
string_nodiscard string_constexpr bool starts_with( StringT const & text, SubT const & seek ) string_noexcept
|
|
{
|
|
typedef typename StringT::value_type A;
|
|
|
|
return detail::starts_with( text, seek, std::equal_to<A>() );
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_nodiscard string_constexpr bool starts_with( StringT const & text, char const * seek ) string_noexcept
|
|
{
|
|
typedef typename StringT::value_type A;
|
|
|
|
return detail::starts_with( text, seek, std::equal_to<A>() );
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_nodiscard string_constexpr14 bool starts_with( StringT const & text, char seek ) string_noexcept
|
|
{
|
|
char look[] = { seek, nullchr<char>() };
|
|
|
|
return detail::starts_with( text, StringT(look), std::equal_to<char>() );
|
|
}
|
|
|
|
#endif // string_TEST_STRING_STARTS_WITH
|
|
|
|
// ends_with():
|
|
|
|
#if string_TEST_STRING_ENDS_WITH
|
|
|
|
template< typename StringT, typename SubT
|
|
string_ENABLE_IF_HAS_METHOD_(StringT, ends_with)
|
|
>
|
|
string_nodiscard string_constexpr bool ends_with( StringT const & text, SubT const & seek ) string_noexcept
|
|
{
|
|
return text.ends_with( seek );
|
|
}
|
|
|
|
template< typename StringT, typename SubT
|
|
string_DISABLE_IF_HAS_METHOD_(StringT, ends_with)
|
|
string_ENABLE_IF_( !std::is_arithmetic<SubT>::value )
|
|
>
|
|
string_nodiscard string_constexpr bool ends_with( StringT const & text, SubT const & seek ) string_noexcept
|
|
{
|
|
typedef typename StringT::value_type A;
|
|
|
|
return detail::ends_with( text, StringT(seek), std::equal_to<A>() );
|
|
}
|
|
|
|
template< typename StringT, typename CharT
|
|
string_DISABLE_IF_HAS_METHOD_(StringT, ends_with)
|
|
string_ENABLE_IF_( std::is_arithmetic<CharT>::value )
|
|
>
|
|
string_nodiscard string_constexpr14 bool ends_with( StringT const & text, CharT seek ) string_noexcept
|
|
{
|
|
CharT look[] = { seek, nullchr<CharT>() };
|
|
|
|
return detail::ends_with( text, StringT(look), std::equal_to<CharT>() );
|
|
}
|
|
|
|
#else // string_TEST_STRING_ENDS_WITH
|
|
|
|
template< typename StringT, typename SubT >
|
|
string_nodiscard string_constexpr bool ends_with( StringT const & text, SubT const & seek ) string_noexcept
|
|
{
|
|
typedef typename StringT::value_type A;
|
|
|
|
return detail::ends_with( text, seek, std::equal_to<A>() );
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_nodiscard string_constexpr bool ends_with( StringT const & text, char const * seek ) string_noexcept
|
|
{
|
|
typedef typename StringT::value_type A;
|
|
|
|
return detail::ends_with( text, StringT(seek), std::equal_to<A>() );
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_nodiscard string_constexpr bool ends_with( StringT const & text, char seek ) string_noexcept
|
|
{
|
|
char look[] = { seek, nullchr<char>() };
|
|
|
|
return detail::ends_with( text, StringT(look), std::equal_to<char>() );
|
|
}
|
|
|
|
#endif // string_TEST_STRING_ENDS_WITH
|
|
|
|
// Modifiers:
|
|
|
|
// replace_all():
|
|
|
|
template< typename CharT, typename FromT, typename ToT
|
|
// string_ENABLE_IF_HAS_METHOD_(StringT, replace)
|
|
>
|
|
string_nodiscard string_constexpr std::basic_string<CharT> &
|
|
replace_all( std::basic_string<CharT> & text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
return detail::replace_all( text, from, to );
|
|
}
|
|
|
|
template< typename StringT, typename FromT, typename ToT
|
|
string_ENABLE_IF_HAS_METHOD_(StringT, begin)
|
|
>
|
|
string_nodiscard string_constexpr StringT &
|
|
replace_all( StringT & text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
return detail::replace_all( text, from, to );
|
|
}
|
|
|
|
// replaced_all():
|
|
|
|
template< typename CharT, typename FromT, typename ToT >
|
|
string_nodiscard string_constexpr std::basic_string<CharT>
|
|
replaced_all( CharT const * text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
std::basic_string<CharT> result( text );
|
|
|
|
return replace_all( result, from, to );
|
|
}
|
|
|
|
template< typename StringT, typename FromT, typename ToT >
|
|
string_nodiscard string_constexpr StringT
|
|
replaced_all( StringT const & text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
StringT result( text );
|
|
|
|
return replace_all( result, from, to );
|
|
}
|
|
|
|
// replace_first():
|
|
|
|
template< typename CharT, typename FromT, typename ToT >
|
|
string_nodiscard string_constexpr CharT *
|
|
replace_first( CharT * text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
return detail::replace_first( text, from, to );
|
|
}
|
|
|
|
template< typename CharT, typename FromT, typename ToT >
|
|
string_nodiscard string_constexpr std::basic_string<CharT> &
|
|
replace_first( std::basic_string<CharT> & text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
return detail::replace_first( text, from, to );
|
|
}
|
|
|
|
template< typename StringT, typename FromT, typename ToT
|
|
string_ENABLE_IF_HAS_METHOD_(StringT, begin)
|
|
>
|
|
string_nodiscard string_constexpr StringT &
|
|
replace_first( StringT & text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
return detail::replace_first( text, from, to );
|
|
}
|
|
|
|
// replaced_first():
|
|
|
|
template< typename CharT, typename FromT, typename ToT >
|
|
string_nodiscard string_constexpr std::basic_string<CharT>
|
|
replaced_first( CharT const * text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
std::basic_string<CharT> result( text );
|
|
|
|
return replace_first( result, from, to );
|
|
}
|
|
|
|
template< typename StringT, typename FromT, typename ToT >
|
|
string_nodiscard string_constexpr StringT
|
|
replaced_first( StringT const & text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
StringT result( text );
|
|
|
|
return replace_first( result, from, to );
|
|
}
|
|
|
|
// replace_last():
|
|
|
|
template< typename CharT, typename FromT, typename ToT >
|
|
string_nodiscard string_constexpr CharT *
|
|
replace_last( CharT * text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
return detail::replace_last( text, from, to );
|
|
}
|
|
|
|
template< typename CharT, typename FromT, typename ToT >
|
|
string_nodiscard string_constexpr std::basic_string<CharT> &
|
|
replace_last( std::basic_string<CharT> & text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
return detail::replace_last( text, from, to );
|
|
}
|
|
|
|
template< typename StringT, typename FromT, typename ToT
|
|
string_ENABLE_IF_HAS_METHOD_(StringT, begin)
|
|
>
|
|
string_nodiscard string_constexpr StringT &
|
|
replace_last( StringT & text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
return detail::replace_last( text, from, to );
|
|
}
|
|
|
|
// replaced_last():
|
|
|
|
template< typename CharT, typename FromT, typename ToT >
|
|
string_nodiscard string_constexpr std::basic_string<CharT>
|
|
replaced_last( CharT const * text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
std::basic_string<CharT> result( text );
|
|
|
|
return replace_last( result, from, to );
|
|
}
|
|
|
|
template< typename StringT, typename FromT, typename ToT >
|
|
string_nodiscard string_constexpr StringT
|
|
replaced_last( StringT const & text, FromT const & from, ToT const & to ) string_noexcept
|
|
{
|
|
StringT result( text );
|
|
|
|
return replace_last( result, from, to );
|
|
}
|
|
|
|
// clear():
|
|
|
|
template< typename CharT >
|
|
CharT * clear( CharT * cp ) string_noexcept
|
|
{
|
|
*cp = nullchr<CharT>();
|
|
return cp;
|
|
}
|
|
|
|
template< typename StringT
|
|
string_ENABLE_IF_HAS_METHOD_(StringT, clear)
|
|
>
|
|
StringT & clear( StringT & text ) string_noexcept
|
|
{
|
|
text.clear();
|
|
return text;
|
|
}
|
|
|
|
// to_lowercase(), to_uppercase():
|
|
|
|
template< typename CharT >
|
|
CharT * to_lowercase( CharT * cp ) string_noexcept
|
|
{
|
|
return detail::to_case( cp, detail::as_lowercase<CharT> );
|
|
}
|
|
|
|
template< typename CharT >
|
|
CharT * to_uppercase( CharT * cp ) string_noexcept
|
|
{
|
|
return detail::to_case( cp, detail::as_uppercase<CharT> );
|
|
}
|
|
|
|
template< typename StringT
|
|
string_ENABLE_IF_HAS_METHOD_(StringT, begin)
|
|
>
|
|
StringT & to_lowercase( StringT & text ) string_noexcept
|
|
{
|
|
return detail::to_case( text, detail::as_lowercase<typename StringT::value_type> );
|
|
}
|
|
|
|
template< typename StringT
|
|
string_ENABLE_IF_HAS_METHOD_(StringT, begin)
|
|
>
|
|
StringT & to_uppercase( StringT & text ) string_noexcept
|
|
{
|
|
return detail::to_case( text, detail::as_uppercase<typename StringT::value_type> );
|
|
}
|
|
|
|
template< typename StringT
|
|
string_ENABLE_IF_HAS_METHOD_(StringT, begin)
|
|
>
|
|
string_nodiscard StringT as_lowercase( StringT const & text ) string_noexcept
|
|
{
|
|
StringT result( text );
|
|
to_lowercase( result );
|
|
return result;
|
|
}
|
|
|
|
template< typename StringT
|
|
string_ENABLE_IF_HAS_METHOD_(StringT, begin)
|
|
>
|
|
string_nodiscard StringT as_uppercase( StringT const & text ) string_noexcept
|
|
{
|
|
StringT result( text );
|
|
to_uppercase( result );
|
|
return result;
|
|
}
|
|
|
|
// append():
|
|
|
|
template< typename CharT, typename TailT >
|
|
string_constexpr CharT *
|
|
append( CharT * text, TailT const & tail ) string_noexcept
|
|
{
|
|
return detail::append( text, tail );
|
|
}
|
|
|
|
template< typename StringT, typename TailT >
|
|
string_constexpr StringT &
|
|
append( StringT & text, TailT const & tail ) string_noexcept
|
|
{
|
|
return detail::append( text, tail );
|
|
}
|
|
|
|
// appended():
|
|
|
|
template< typename StringT, typename TailT
|
|
string_ENABLE_IF_HAS_METHOD_(StringT, begin)
|
|
>
|
|
string_nodiscard string_constexpr StringT
|
|
appended( StringT const & text, TailT const & tail ) string_noexcept
|
|
{
|
|
StringT result( text );
|
|
|
|
return detail::append( result, tail );
|
|
}
|
|
|
|
// TODO trim()
|
|
|
|
// trim_left():
|
|
|
|
template< typename StringT >
|
|
inline StringT const default_trim_set()
|
|
{
|
|
return " \t\n";
|
|
}
|
|
|
|
template< typename CharT, typename SetT >
|
|
string_constexpr CharT *
|
|
trim_left( CharT * text, SetT const * set ) string_noexcept
|
|
{
|
|
return detail::trim_left( text, set );
|
|
}
|
|
|
|
template< typename CharT >
|
|
string_constexpr CharT *
|
|
trim_left( CharT * text ) string_noexcept
|
|
{
|
|
return trim_left( text, default_trim_set<CharT const *>() );
|
|
}
|
|
|
|
template< typename StringT, typename SetT >
|
|
string_constexpr StringT &
|
|
trim_left( StringT & text, SetT const & set ) string_noexcept
|
|
{
|
|
return detail::trim_left( text, set );
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_constexpr StringT &
|
|
trim_left( StringT & text ) string_noexcept
|
|
{
|
|
return trim_left( text, default_trim_set<StringT>() );
|
|
}
|
|
|
|
template< typename StringT, typename SetT >
|
|
string_constexpr14 StringT
|
|
trimmed_left( StringT const & text, SetT const & set ) string_noexcept
|
|
{
|
|
StringT result( text );
|
|
return detail::trim_left( result, set );
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_constexpr StringT
|
|
trimmed_left( StringT const & text ) string_noexcept
|
|
{
|
|
return trimmed_left( text, default_trim_set<StringT>() );
|
|
}
|
|
|
|
// trim_right():
|
|
|
|
template< typename CharT, typename SetT >
|
|
string_constexpr CharT *
|
|
trim_right( CharT * text, SetT const * set ) string_noexcept
|
|
{
|
|
return detail::trim_right( text, set );
|
|
}
|
|
|
|
template< typename CharT >
|
|
string_constexpr CharT *
|
|
trim_right( CharT * text ) string_noexcept
|
|
{
|
|
return trim_right( text, default_trim_set<CharT const *>() );
|
|
}
|
|
|
|
template< typename StringT, typename SetT >
|
|
string_constexpr StringT &
|
|
trim_right( StringT & text, SetT const & set ) string_noexcept
|
|
{
|
|
return detail::trim_right( text, set );
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_constexpr StringT &
|
|
trim_right( StringT & text ) string_noexcept
|
|
{
|
|
return trim_right( text, default_trim_set<StringT>() );
|
|
}
|
|
|
|
template< typename StringT, typename SetT >
|
|
string_constexpr14 StringT
|
|
trimmed_right( StringT const & text, SetT const & set ) string_noexcept
|
|
{
|
|
StringT result( text );
|
|
return detail::trim_right( result, set );
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_constexpr StringT
|
|
trimmed_right( StringT const & text ) string_noexcept
|
|
{
|
|
return trimmed_right( text, default_trim_set<StringT>() );
|
|
}
|
|
|
|
// trim():
|
|
|
|
template< typename CharT, typename SetT >
|
|
string_constexpr CharT *
|
|
trim( CharT * text, SetT const * set ) string_noexcept
|
|
{
|
|
return detail::trim( text, set );
|
|
}
|
|
|
|
template< typename CharT >
|
|
string_constexpr CharT *
|
|
trim( CharT * text ) string_noexcept
|
|
{
|
|
return trim( text, default_trim_set<CharT const *>() );
|
|
}
|
|
|
|
template< typename StringT, typename SetT >
|
|
string_constexpr StringT &
|
|
trim( StringT & text, SetT const & set ) string_noexcept
|
|
{
|
|
return detail::trim( text, set );
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_constexpr StringT &
|
|
trim( StringT & text ) string_noexcept
|
|
{
|
|
return trim( text, default_trim_set<StringT>() );
|
|
}
|
|
|
|
template< typename StringT, typename SetT >
|
|
string_constexpr StringT
|
|
trimmed( StringT const & text, SetT const & set ) string_noexcept
|
|
{
|
|
StringT result( text );
|
|
return detail::trim( result, set );
|
|
}
|
|
|
|
template< typename StringT >
|
|
string_constexpr StringT
|
|
trimmed( StringT const & text ) string_noexcept
|
|
{
|
|
return trimmed( text, default_trim_set<StringT>() );
|
|
}
|
|
|
|
// TODO join():
|
|
|
|
// Note: add way to defined return type:
|
|
|
|
template< typename Coll, typename SepT
|
|
// string_ENABLE_IF_( !std::is_pointer<typename Coll::value_type>::value )
|
|
>
|
|
string_nodiscard string_constexpr14
|
|
typename Coll::value_type
|
|
join( Coll const & coll, SepT const & sep ) string_noexcept
|
|
{
|
|
typename Coll::value_type result;
|
|
|
|
typename Coll::const_iterator const coll_begin = string::cbegin(coll);
|
|
typename Coll::const_iterator const coll_end = string::cend(coll);
|
|
|
|
typename Coll::const_iterator pos = coll_begin;
|
|
|
|
if ( pos != coll_end )
|
|
{
|
|
append( result, *pos++ );
|
|
}
|
|
|
|
for ( ; pos != coll_end; ++pos )
|
|
{
|
|
append( append( result, sep ), *pos );
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// split():
|
|
|
|
// Various kinds of delimiters:
|
|
// - literal_delimiter - a single string delimiter
|
|
// - any_of_delimiter - any of given characters as delimiter
|
|
// - fixed_delimiter - fixed length delimiter
|
|
// - limit_delimiter - not implemented
|
|
// - regex_delimiter - regular expression delimiter
|
|
// - char_delimiter - single-char delimiter
|
|
|
|
template< typename CharT >
|
|
basic_string_view<CharT> basic_delimiter_end(basic_string_view<CharT> sv)
|
|
{
|
|
#if string_CONFIG_SELECT_STRING_VIEW != string_CONFIG_SELECT_STRING_VIEW_STD
|
|
return basic_string_view<CharT>(sv.cend(), size_t(0));
|
|
#else
|
|
return basic_string_view<CharT>(sv.data() + sv.size(), size_t(0));
|
|
#endif
|
|
}
|
|
|
|
// a single string delimiter:
|
|
|
|
template< typename CharT >
|
|
class basic_literal_delimiter
|
|
{
|
|
const std::basic_string<CharT> delimiter_;
|
|
mutable size_t found_;
|
|
|
|
public:
|
|
explicit basic_literal_delimiter(basic_string_view<CharT> sv)
|
|
: delimiter_(to_string(sv))
|
|
, found_(0)
|
|
{}
|
|
|
|
size_t length() const
|
|
{
|
|
return delimiter_.length();
|
|
}
|
|
|
|
basic_string_view<CharT> operator()(basic_string_view<CharT> text, size_t pos) const
|
|
{
|
|
return find(text, pos);
|
|
}
|
|
|
|
basic_string_view<CharT> find(basic_string_view<CharT> text, size_t pos) const
|
|
{
|
|
// out of range, return 'empty' if last match was at end of text, else return 'done':
|
|
if ( pos >= text.length())
|
|
{
|
|
// last delimiter match at end of text?
|
|
if ( found_ != text.length() - 1 )
|
|
return basic_delimiter_end(text);
|
|
|
|
found_ = 0;
|
|
return text.substr(text.length() - 1, 0);
|
|
}
|
|
|
|
// a single character at a time:
|
|
if (0 == delimiter_.length())
|
|
{
|
|
return text.substr(pos, 1);
|
|
}
|
|
|
|
found_ = text.find(delimiter_, pos);
|
|
|
|
// at a delimiter, or searching past the last delimiter:
|
|
if (found_ == pos || pos == text.length())
|
|
{
|
|
return text.substr(pos, 0);
|
|
}
|
|
|
|
// no delimiter found:
|
|
if (found_ == basic_string_view<CharT>::npos)
|
|
{
|
|
// return remaining text:
|
|
if (pos < text.length())
|
|
{
|
|
return text.substr(pos);
|
|
}
|
|
|
|
// nothing left, return 'done':
|
|
return basic_delimiter_end(text);
|
|
}
|
|
|
|
// delimited text:
|
|
return text.substr(pos, found_ - pos);
|
|
}
|
|
};
|
|
|
|
// any of given characters as delimiter:
|
|
|
|
template< typename CharT >
|
|
class basic_any_of_delimiter
|
|
{
|
|
const std::basic_string<CharT> delimiters_;
|
|
|
|
public:
|
|
explicit basic_any_of_delimiter(basic_string_view<CharT> sv)
|
|
: delimiters_(to_string(sv)) {}
|
|
|
|
size_t length() const
|
|
{
|
|
return (min)( size_t(1), delimiters_.length());
|
|
}
|
|
|
|
basic_string_view<CharT> operator()(basic_string_view<CharT> text, size_t pos) const
|
|
{
|
|
return find(text, pos);
|
|
}
|
|
|
|
basic_string_view<CharT> find(basic_string_view<CharT> text, size_t pos) const
|
|
{
|
|
// out of range, return 'done':
|
|
if ( pos > text.length())
|
|
return basic_delimiter_end(text);
|
|
|
|
// a single character at a time:
|
|
if (0 == delimiters_.length())
|
|
{
|
|
return text.substr(pos, 1);
|
|
}
|
|
|
|
size_t found = text.find_first_of(delimiters_, pos);
|
|
|
|
// at a delimiter, or searching past the last delimiter:
|
|
if (found == pos || (pos == text.length()))
|
|
{
|
|
return basic_string_view<CharT>();
|
|
}
|
|
|
|
// no delimiter found:
|
|
if (found == basic_string_view<CharT>::npos)
|
|
{
|
|
// return remaining text:
|
|
if (pos < text.length())
|
|
{
|
|
return text.substr(pos);
|
|
}
|
|
|
|
// nothing left, return 'done':
|
|
return basic_delimiter_end(text);
|
|
}
|
|
|
|
// delimited text:
|
|
return text.substr(pos, found - pos);
|
|
}
|
|
};
|
|
|
|
// fixed length delimiter:
|
|
|
|
template< typename CharT >
|
|
class basic_fixed_delimiter
|
|
{
|
|
size_t len_;
|
|
|
|
public:
|
|
explicit basic_fixed_delimiter(size_t len)
|
|
: len_(len) {}
|
|
|
|
size_t length() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
string_view operator()(basic_string_view<CharT> text, size_t pos) const
|
|
{
|
|
return find(text, pos);
|
|
}
|
|
|
|
string_view find(basic_string_view<CharT> text, size_t pos) const
|
|
{
|
|
// out of range, return 'done':
|
|
if ( pos > text.length())
|
|
return basic_delimiter_end(text);
|
|
|
|
// current slice:
|
|
return text.substr(pos, len_);
|
|
}
|
|
};
|
|
|
|
// TODO limit_delimiter - Delimiter template would take another Delimiter and a size_t limiting
|
|
// the given delimiter to matching a max numbers of times. This is similar to the 3rd argument to
|
|
// perl's split() function.
|
|
|
|
template< typename CharT, typename DelimiterT >
|
|
class basic_limit_delimiter;
|
|
|
|
// regular expression delimiter:
|
|
|
|
#if string_HAVE_REGEX
|
|
|
|
template< typename CharT >
|
|
class basic_regex_delimiter
|
|
{
|
|
std::regex delimiter_re_; // regular expression designating delimiters
|
|
size_t delimiter_len_; // length of regular expression
|
|
mutable size_t matched_delimiter_length_; // length of the actually matched delimiter
|
|
mutable bool trailing_delimiter_seen; // whether to provide last empty result
|
|
|
|
public:
|
|
explicit basic_regex_delimiter(basic_string_view<CharT> sv)
|
|
: delimiter_re_(to_string(sv))
|
|
, delimiter_len_(sv.length())
|
|
, matched_delimiter_length_(0u)
|
|
, trailing_delimiter_seen(false)
|
|
{}
|
|
|
|
size_t length() const
|
|
{
|
|
return matched_delimiter_length_;
|
|
}
|
|
|
|
basic_string_view<CharT> operator()(basic_string_view<CharT> text, size_t pos) const
|
|
{
|
|
return find(text, pos);
|
|
}
|
|
|
|
basic_string_view<CharT> find(basic_string_view<CharT> text, size_t pos) const
|
|
{
|
|
// trailing empty entry:
|
|
// TODO this feels like a hack, don't know any better at this moment
|
|
if (trailing_delimiter_seen)
|
|
{
|
|
trailing_delimiter_seen = false;
|
|
return basic_string_view<CharT>();
|
|
}
|
|
|
|
// out of range, return 'done':
|
|
if ( pos > text.length())
|
|
return basic_delimiter_end(text);
|
|
|
|
// a single character at a time:
|
|
if (0 == delimiter_len_)
|
|
{
|
|
return text.substr(pos, 1);
|
|
}
|
|
|
|
std::smatch m;
|
|
std::basic_string<CharT> s = to_string(text.substr(pos));
|
|
|
|
const bool found = std::regex_search(s, m, delimiter_re_);
|
|
|
|
matched_delimiter_length_ = m.length();
|
|
|
|
// no delimiter found:
|
|
if (!found)
|
|
{
|
|
// return remaining text:
|
|
if (pos < text.length())
|
|
{
|
|
return text.substr(pos);
|
|
}
|
|
|
|
// nothing left, return 'done':
|
|
return basic_delimiter_end(text);
|
|
}
|
|
|
|
// at a trailing delimiter, remember for next round:
|
|
else if ((size_t(m.position()) == s.length() - 1))
|
|
{
|
|
trailing_delimiter_seen = true;
|
|
}
|
|
|
|
// delimited text, the match in the input string:
|
|
return text.substr(pos, m.position());
|
|
}
|
|
};
|
|
|
|
#endif // string_HAVE_REGEX
|
|
|
|
// single-char delimiter:
|
|
|
|
template< typename CharT >
|
|
class basic_char_delimiter
|
|
{
|
|
CharT c_;
|
|
|
|
public:
|
|
explicit basic_char_delimiter(CharT c)
|
|
: c_(c) {}
|
|
|
|
size_t length() const
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
basic_string_view<CharT> operator()(basic_string_view<CharT> text, size_t pos) const
|
|
{
|
|
return find(text, pos);
|
|
}
|
|
|
|
basic_string_view<CharT> find(basic_string_view<CharT> text, size_t pos) const
|
|
{
|
|
size_t found = text.find(c_, pos);
|
|
|
|
// nothing left, return 'done':
|
|
if (found == basic_string_view<CharT>::npos)
|
|
return basic_delimiter_end(text);
|
|
|
|
// the c_ in the input string:
|
|
return text.substr(found, 1);
|
|
}
|
|
};
|
|
|
|
// typedefs:
|
|
|
|
typedef basic_literal_delimiter< char > literal_delimiter;
|
|
typedef basic_literal_delimiter<wchar_t> wliteral_delimiter;
|
|
typedef basic_any_of_delimiter< char > any_of_delimiter;
|
|
typedef basic_any_of_delimiter< wchar_t> wany_of_delimiter;
|
|
typedef basic_fixed_delimiter< char > fixed_delimiter;
|
|
typedef basic_fixed_delimiter< wchar_t> wfixed_delimiter;
|
|
typedef basic_char_delimiter< char > char_delimiter;
|
|
typedef basic_char_delimiter< wchar_t> wchar_t_delimiter;
|
|
// typedef basic_limit_delimiter< char > limit_delimiter;
|
|
// typedef basic_limit_delimiter< wchar_t> wlimit_delimiter;
|
|
|
|
#if string_HAVE_REGEX
|
|
typedef basic_regex_delimiter< char > regex_delimiter;
|
|
typedef basic_regex_delimiter< wchar_t> wregex_delimiter;
|
|
#endif
|
|
|
|
#if string_HAVE_CHAR16_T
|
|
|
|
typedef basic_literal_delimiter<char16_t> u16literal_delimiter;
|
|
typedef basic_literal_delimiter<char32_t> u32literal_delimiter;
|
|
typedef basic_any_of_delimiter< char16_t> u16any_of_delimiter;
|
|
typedef basic_any_of_delimiter< char32_t> u32any_of_delimiter;
|
|
typedef basic_fixed_delimiter< char16_t> u16fixed_delimiter;
|
|
typedef basic_fixed_delimiter< char32_t> u32fixed_delimiter;
|
|
typedef basic_char_delimiter< char16_t> u16char_delimiter;
|
|
typedef basic_char_delimiter< char32_t> u32char_delimiter;
|
|
// typedef basic_limit_delimiter< char16_t> u16limit_delimiter;
|
|
// typedef basic_limit_delimiter< char32_t> u32limit_delimiter;
|
|
|
|
#if string_HAVE_REGEX
|
|
typedef basic_regex_delimiter< char16_t> u16regex_delimiter;
|
|
typedef basic_regex_delimiter< char32_t> u32regex_delimiter;
|
|
#endif
|
|
|
|
#endif // string_HAVE_CHAR16_T
|
|
|
|
// split():
|
|
|
|
template<typename Delimiter> std::vector< string_view> split( string_view text, Delimiter delimiter) { return detail::split(text, delimiter); }
|
|
template<typename Delimiter> std::vector<wstring_view> split(wstring_view text, Delimiter delimiter) { return detail::split(text, delimiter); }
|
|
|
|
#if string_HAVE_CHAR16_T
|
|
template<typename Delimiter> std::vector<u16string_view> split(u16string_view text, Delimiter delimiter) { return detail::split(text, delimiter); }
|
|
template<typename Delimiter> std::vector<u32string_view> split(u32string_view text, Delimiter delimiter) { return detail::split(text, delimiter); }
|
|
#endif
|
|
|
|
inline std::vector<string_view> split(string_view text, char const * d) { return detail::split(text, literal_delimiter(d)); }
|
|
|
|
} // namespace string
|
|
} // namespace nonstd
|
|
|
|
namespace nonstd {
|
|
|
|
// using string::clear;
|
|
|
|
} // namespace nonstd
|
|
|
|
#endif // NONSTD_STRING_LITE_HPP
|