carb/cpp/StringView.h

File members: carb/cpp/StringView.h

// Copyright (c) 2020-2024, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

#pragma once

#include "../Defines.h"

#include "../../omni/detail/PointerIterator.h"
#include "TypeTraits.h"

#include <algorithm>
#include <string>
#include <typeindex> // for std::hash
#include <utility>

namespace carb
{
namespace cpp
{

template <class CharT, class Traits>
class basic_string_view;

namespace detail
{

template <class T>
struct IsBasicStringView : public std::false_type
{
};

template <class T, class Traits>
struct IsBasicStringView<basic_string_view<T, Traits>> : public std::true_type
{
};

template <class T, class OpType, class = void>
struct HasOperator : public std::false_type
{
};

template <class T, class OpType>
struct HasOperator<T, OpType, void_t<decltype(std::declval<T>().operator OpType())>> : public std::true_type
{
};

template <class CharT, class T>
struct IsCharArrayLiteral : public std::false_type
{
};

template <class CharT, size_t N>
struct IsCharArrayLiteral<CharT, const CharT (&)[N]> : public std::true_type
{
};

// GCC instantiates some functions always so they cannot use static_assert, but throwing an exception is allowed from a
// constexpr function (which will act as a compile failure if constexpr) so fall back to that.
#if CARB_EXCEPTIONS_ENABLED
#    define CARB_ALWAYS_FAIL(msg) throw std::out_of_range(msg)
#    define CARB_THROW_OR_CHECK(check, msg)                                                                            \
        if (!CARB_LIKELY(check))                                                                                       \
        throw std::out_of_range(msg)
#else
#    define CARB_THROW_OR_CHECK(check, msg) CARB_CHECK((check), msg)
#    if CARB_COMPILER_MSC
#        define CARB_ALWAYS_FAIL(msg) static_assert(false, msg)
#    else
#        define CARB_ALWAYS_FAIL(msg) CARB_FATAL_UNLESS(false, msg)
#    endif
#endif

} // namespace detail

template <class CharT, class Traits = std::char_traits<CharT>>
class CARB_VIZ basic_string_view
{
public:
    using traits_type = Traits;
    using value_type = CharT;
    using pointer = CharT*;
    using const_pointer = const CharT*;
    using reference = CharT&;
    using const_reference = const CharT&;
    using const_iterator = omni::detail::PointerIterator<const_pointer, basic_string_view>;
    using iterator = const_iterator;
    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
    using reverse_iterator = const_reverse_iterator;
    using size_type = std::size_t;
    using difference_type = std::ptrdiff_t;

    static constexpr size_type npos = size_type(-1);

    constexpr basic_string_view() noexcept = default;

    constexpr basic_string_view(const basic_string_view& other) noexcept = default;

    constexpr basic_string_view(const CharT* s, size_type count) : m_data(s), m_count(count)
    {
    }

#ifdef DOXYGEN_BUILD
    constexpr basic_string_view(const CharT* s) : m_data(s), m_count(traits_type::length(s))
#else
    template <class T,
              std::enable_if_t<!detail::IsCharArrayLiteral<CharT, T>::value && std::is_convertible<T, const_pointer>::value,
                               bool> = false>
    constexpr basic_string_view(T&& s) : m_data(s), m_count(traits_type::length(s))
#endif
    {
    }

    template <size_t N>
    constexpr basic_string_view(const CharT (&literal)[N]) noexcept : m_data(literal), m_count(N - 1)
    {
    }

    template <class It CARB_NO_DOC(
        ,
        std::enable_if_t<std::is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value,
                         bool> = false)>
    constexpr basic_string_view(It first, It last) : m_data(std::addressof(*first)), m_count(std::distance(first, last))
    {
    }

    template <class R CARB_NO_DOC(,
                                  std::enable_if_t<cpp::detail::IsConvertibleRange<cpp::remove_cvref_t<R>, const_pointer>::value &&
                                                       !detail::IsBasicStringView<cpp::remove_cvref_t<R>>::value &&
                                                       !std::is_convertible<R, const char*>::value &&
                                                       !detail::HasOperator<cpp::remove_cvref_t<R>, basic_string_view>::value,
                                                   bool> = false)>
    constexpr explicit basic_string_view(R&& r) : m_data(r.data()), m_count(r.size())
    {
    }

    template <class S,
              class Allocator = typename S::allocator_type,
              std::enable_if_t<std::is_same<std::basic_string<CharT, Traits, Allocator>, std::decay_t<S>>::value, bool> = false>
    constexpr basic_string_view(const S& s) : m_data(s.data()), m_count(s.size())
    {
    }

    constexpr basic_string_view(std::nullptr_t) = delete;

    constexpr basic_string_view& operator=(const basic_string_view& view) noexcept = default;

    constexpr const_iterator begin() const noexcept
    {
        return const_iterator(m_data);
    }

    constexpr const_iterator cbegin() const noexcept
    {
        return const_iterator(m_data);
    }

    constexpr const_iterator end() const noexcept
    {
        return const_iterator(m_data + m_count);
    }

    constexpr const_iterator cend() const noexcept
    {
        return const_iterator(m_data + m_count);
    }

    constexpr const_reverse_iterator rbegin() const noexcept
    {
        return const_reverse_iterator(end());
    }

    constexpr const_reverse_iterator crbegin() const noexcept
    {
        return const_reverse_iterator(cend());
    }

    constexpr const_reverse_iterator rend() const noexcept
    {
        return const_reverse_iterator(begin());
    }

    constexpr const_reverse_iterator crend() const noexcept
    {
        return const_reverse_iterator(cbegin());
    }

    constexpr const_reference operator[](size_type pos) const noexcept /*strengthened*/
    {
        // Though the standard says no bounds checking we do assert
        CARB_ASSERT(pos < m_count);
        return m_data[pos];
    }

    constexpr const_reference at(size_type pos) const
    {
        CARB_THROW_OR_CHECK(pos < m_count, "pos >= size()");
        return m_data[pos];
    }

    constexpr const_reference front() const noexcept /*strengthened*/
    {
        CARB_ASSERT(!empty(), "Undefined since empty()");
        return *m_data;
    }

    constexpr const_reference back() const noexcept /*strengthened*/
    {
        CARB_ASSERT(!empty(), "Undefined since empty()");
        return m_data[m_count - 1];
    }

    constexpr const_pointer data() const noexcept
    {
        return m_data;
    }

    constexpr size_type size() const noexcept
    {
        return m_count;
    }

    constexpr size_type length() const noexcept
    {
        return m_count;
    }

    constexpr size_type max_size() const noexcept
    {
        return npos - 1;
    }

    CARB_NODISCARD constexpr bool empty() const noexcept
    {
        return m_count == 0;
    }

    constexpr void remove_prefix(size_type n) noexcept /*strengthened*/
    {
        CARB_ASSERT(n <= size(), "Undefined since n > size()");
        m_data += n;
        m_count -= n;
    }

    constexpr void remove_suffix(size_type n) noexcept /*strengthened*/
    {
        CARB_ASSERT(n <= size(), "Undefined since n > size()");
        m_count = m_count - n;
    }

    constexpr void swap(basic_string_view& v) noexcept
    {
        std::swap(m_data, v.m_data);
        std::swap(m_count, v.m_count);
    }

    constexpr size_type copy(CharT* dest, size_type count, size_type pos = 0) const
    {
        CARB_THROW_OR_CHECK(pos <= size(), "pos > size()");
        size_type rcount = ::carb_min(count, size() - pos);
        Traits::copy(dest, m_data + pos, rcount);
        return rcount;
    }

    constexpr basic_string_view substr(size_t pos, size_t count = npos) const
    {
        CARB_THROW_OR_CHECK(pos <= size(), "pos > size()");
        size_t rcount = ::carb_min(count, size() - pos);
        return { m_data + pos, rcount };
    }

    constexpr int compare(basic_string_view v) const noexcept
    {
        size_type rlen = ::carb_min(size(), v.size());
        int result = traits_type::compare(data(), v.data(), rlen);
        if (result != 0 || size() == v.size())
            return result;
        return (size() < v.size()) ? -1 : 1;
    }

    constexpr int compare(size_type pos1, size_type count1, basic_string_view v) const
    {
        return substr(pos1, count1).compare(v);
    }

    constexpr int compare(size_type pos1, size_type count1, basic_string_view v, size_type pos2, size_type count2) const
    {
        return substr(pos1, count1).compare(v.substr(pos2, count2));
    }

    constexpr int compare(const CharT* s) const
    {
        return compare(basic_string_view(s));
    }

    constexpr int compare(size_type pos1, size_type count1, const CharT* s) const
    {
        return substr(pos1, count1).compare(basic_string_view(s));
    }

    constexpr int compare(size_type pos1, size_type count1, const CharT* s, size_type count2) const
    {
        return substr(pos1, count1).compare(basic_string_view(s, count2));
    }

    constexpr bool starts_with(basic_string_view sv) const noexcept
    {
        return substr(0, sv.size()) == sv;
    }

    constexpr bool starts_with(CharT ch) const noexcept
    {
        return !empty() && traits_type::eq(front(), ch);
    }

    constexpr bool starts_with(const CharT* s) const
    {
        return starts_with(basic_string_view(s));
    }

    constexpr bool ends_with(basic_string_view sv) const noexcept
    {
        return size() >= sv.size() && compare(size() - sv.size(), npos, sv) == 0;
    }

    constexpr bool ends_with(CharT ch) const noexcept
    {
        return !empty() && traits_type::eq(back(), ch);
    }

    constexpr bool ends_with(const CharT* s) const
    {
        return ends_with(basic_string_view(s));
    }

    constexpr bool contains(basic_string_view sv) const noexcept
    {
        return find(sv) != npos;
    }

    constexpr bool contains(CharT c) const noexcept
    {
        return find(c) != npos;
    }

    constexpr bool contains(const CharT* s) const
    {
        return find(s) != npos;
    }

    constexpr size_type find(basic_string_view v, size_type pos = 0) const noexcept
    {
        // [strings.view.find] in the Standard.
        size_type xpos = pos;

        while (xpos + v.size() <= size())
        {
            if (traits_type::compare(v.data(), data() + xpos, v.size()) == 0)
            {
                return xpos;
            }
            xpos++;
        }
        return npos;
    }

    constexpr size_type find(CharT ch, size_type pos = 0) const noexcept
    {
        size_type xpos = pos;

        while (xpos < size())
        {
            if (traits_type::eq(data()[xpos], ch))
            {
                return xpos;
            }
            xpos++;
        }
        return npos;
    }

    constexpr size_type find(const CharT* s, size_type pos, size_type count) const
    {
        return find(basic_string_view(s, count), pos);
    }

    constexpr size_type find(const CharT* s, size_type pos = 0) const
    {
        return find(basic_string_view(s), pos);
    }

    constexpr size_type rfind(basic_string_view v, size_type pos = npos) const noexcept
    {
        if (v.size() > size())
        {
            return npos;
        }

        // Clip the position to our string length.
        for (size_type xpos = ::carb_min(pos, size() - v.size());; xpos--)
        {
            if (traits_type::compare(v.data(), data() + xpos, v.size()) == 0)
            {
                return xpos;
            }
            if (xpos == 0)
            {
                break;
            }
        }
        return npos;
    }

    constexpr size_type rfind(CharT ch, size_type pos = npos) const noexcept
    {
        if (empty())
        {
            return npos;
        }

        // Clip the position to our string length.
        for (size_type xpos = ::carb_min(pos, size() - 1);; xpos--)
        {
            if (traits_type::eq(ch, data()[xpos]))
            {
                return xpos;
            }
            if (xpos == 0)
            {
                break;
            }
        }
        return npos;
    }

    constexpr size_type rfind(const CharT* s, size_type pos, size_type count) const
    {
        return rfind(basic_string_view(s, count), pos);
    }

    constexpr size_type rfind(const CharT* s, size_type pos = npos) const
    {
        return rfind(basic_string_view(s), pos);
    }

    constexpr size_type find_first_of(basic_string_view v, size_type pos = 0) const noexcept
    {
        if (v.empty())
        {
            return npos;
        }
        size_type xpos = pos;

        while (xpos < size())
        {
            if (v.find(m_data[xpos]) != npos)
            {
                return xpos;
            }
            xpos++;
        }
        return npos;
    }

    constexpr size_type find_first_of(CharT ch, size_type pos = 0) const noexcept
    {
        return find(ch, pos);
    }

    constexpr size_type find_first_of(const CharT* s, size_type pos, size_type count) const
    {
        return find_first_of(basic_string_view(s, count), pos);
    }

    constexpr size_type find_first_of(const CharT* s, size_type pos = 0) const
    {
        return find_first_of(basic_string_view(s), pos);
    }

    constexpr size_type find_last_of(basic_string_view v, size_type pos = npos) const noexcept
    {
        if (v.empty() || empty())
        {
            return npos;
        }

        // Clip the position to our string length.
        for (size_type xpos = ::carb_min(pos, size() - 1);; xpos--)
        {
            if (v.find(data()[xpos]) != npos)
            {
                return xpos;
            }
            if (xpos == 0)
            {
                break;
            }
        }
        return npos;
    }

    constexpr size_type find_last_of(CharT ch, size_type pos = npos) const noexcept
    {
        return rfind(ch, pos);
    }

    constexpr size_type find_last_of(const CharT* s, size_type pos, size_type count) const
    {
        return find_last_of(basic_string_view(s, count), pos);
    }

    constexpr size_type find_last_of(const CharT* s, size_type pos = npos) const
    {
        return find_last_of(basic_string_view(s), pos);
    }

    constexpr size_type find_first_not_of(basic_string_view v, size_type pos = 0) const noexcept
    {
        size_type xpos = pos;

        while (xpos < size())
        {
            if (v.find(data()[xpos]) == npos)
            {
                return xpos;
            }
            xpos++;
        }
        return npos;
    }

    constexpr size_type find_first_not_of(CharT ch, size_type pos = 0) const noexcept
    {
        size_type xpos = pos;

        while (xpos < size())
        {
            if (!traits_type::eq(ch, m_data[xpos]))
            {
                return xpos;
            }
            xpos++;
        }
        return npos;
    }

    constexpr size_type find_first_not_of(const CharT* s, size_type pos, size_type count) const
    {
        return find_first_not_of(basic_string_view(s, count), pos);
    }

    constexpr size_type find_first_not_of(const CharT* s, size_type pos = 0) const
    {
        return find_first_not_of(basic_string_view(s), pos);
    }

    constexpr size_type find_last_not_of(basic_string_view v, size_type pos = 0) const noexcept
    {
        if (empty())
        {
            return npos;
        }

        // Clip the position to our string length.
        for (size_type xpos = ::carb_min(pos, size() - 1);; xpos--)
        {
            if (v.find(data()[xpos]) == npos)
            {
                return xpos;
            }
            if (xpos == 0)
            {
                break;
            }
        }
        return npos;
    }

    constexpr size_type find_last_not_of(CharT ch, size_type pos = 0) const noexcept
    {
        if (empty())
        {
            return npos;
        }

        // Clip the position to our string length.
        for (size_type xpos = ::carb_min(pos, size() - 1);; xpos--)
        {
            if (!traits_type::eq(data()[xpos], ch))
            {
                return xpos;
            }
            if (xpos == 0)
            {
                break;
            }
        }
        return npos;
    }

    constexpr size_type find_last_not_of(const CharT* s, size_type pos, size_type count) const
    {
        return find_last_not_of(basic_string_view(s, count), pos);
    }

    constexpr size_type find_last_not_of(const CharT* s, size_type pos = 0) const
    {
        return find_last_not_of(basic_string_view(s), pos);
    }

private:
    CARB_VIZ const_pointer m_data = nullptr;
    CARB_VIZ size_type m_count = 0;
};

using string_view = basic_string_view<char>;
using wstring_view = basic_string_view<wchar_t>;

#if CARB_HAS_CPP20 && defined(__cpp_char8_t)
using u8string_view = basic_string_view<char8_t>;
#endif

using u16string_view = basic_string_view<char16_t>;
using u32string_view = basic_string_view<char32_t>;

// Ensure these for ABI safety
#define CARB_IMPL_ENSURE_ABI(cls)                                                                                      \
    static_assert(std::is_standard_layout<cls>::value, #cls " must be standard layout");                               \
    static_assert(std::is_trivially_copyable<cls>::value, #cls " must be trivially copyable"); /* C++23 requirement */ \
    static_assert(sizeof(cls) == (2 * sizeof(size_t)), #cls "ABI change violation")
CARB_IMPL_ENSURE_ABI(string_view);
CARB_IMPL_ENSURE_ABI(wstring_view);
#if CARB_HAS_CPP20 && defined(__cpp_char8_t)
CARB_IMPL_ENSURE_ABI(u8string_view);
#endif
CARB_IMPL_ENSURE_ABI(u16string_view);
CARB_IMPL_ENSURE_ABI(u32string_view);
#undef CARB_IMPL_ENSURE_ABI

template <class CharT, class Traits = std::char_traits<CharT>>
bool operator==(const basic_string_view<CharT, Traits>& a, const basic_string_view<CharT, Traits>& b)
{
    return a.compare(b) == 0;
}

template <class CharT, class Traits = std::char_traits<CharT>>
bool operator!=(const basic_string_view<CharT, Traits>& a, const basic_string_view<CharT, Traits>& b)
{
    return a.compare(b) != 0;
}

template <class CharT, class Traits = std::char_traits<CharT>>
bool operator<(const basic_string_view<CharT, Traits>& a, const basic_string_view<CharT, Traits>& b)
{
    return a.compare(b) < 0;
}

template <class CharT, class Traits = std::char_traits<CharT>>
bool operator<=(const basic_string_view<CharT, Traits>& a, const basic_string_view<CharT, Traits>& b)
{
    return a.compare(b) <= 0;
}

template <class CharT, class Traits = std::char_traits<CharT>>
bool operator>(const basic_string_view<CharT, Traits>& a, const basic_string_view<CharT, Traits>& b)
{
    return a.compare(b) > 0;
}

template <class CharT, class Traits = std::char_traits<CharT>>
bool operator>=(const basic_string_view<CharT, Traits>& a, const basic_string_view<CharT, Traits>& b)
{
    return a.compare(b) >= 0;
}

// [string.view.comparison]

inline bool operator==(const char* t, string_view sv)
{
    return string_view(t) == sv;
}

inline bool operator==(string_view sv, const char* t)
{
    return sv == string_view(t);
}

inline bool operator!=(const char* t, string_view sv)
{
    return string_view(t) != sv;
}

inline bool operator!=(string_view sv, const char* t)
{
    return sv != string_view(t);
}

inline bool operator<(const char* t, string_view sv)
{
    return string_view(t) < sv;
}

inline bool operator<(string_view sv, const char* t)
{
    return sv < string_view(t);
}

inline bool operator<=(const char* t, string_view sv)
{
    return string_view(t) <= sv;
}

inline bool operator<=(string_view sv, const char* t)
{
    return sv <= string_view(t);
}

inline bool operator>(const char* t, string_view sv)
{
    return string_view(t) > sv;
}

inline bool operator>(string_view sv, const char* t)
{
    return sv > string_view(t);
}

inline bool operator>=(const char* t, string_view sv)
{
    return string_view(t) >= sv;
}

inline bool operator>=(string_view sv, const char* t)
{
    return sv >= string_view(t);
}

inline bool operator==(const wchar_t* t, wstring_view sv)
{
    return wstring_view(t) == sv;
}

inline bool operator==(wstring_view sv, const wchar_t* t)
{
    return sv == wstring_view(t);
}

inline bool operator!=(const wchar_t* t, wstring_view sv)
{
    return wstring_view(t) != sv;
}

inline bool operator!=(wstring_view sv, const wchar_t* t)
{
    return sv != wstring_view(t);
}

inline bool operator<(const wchar_t* t, wstring_view sv)
{
    return wstring_view(t) < sv;
}

inline bool operator<(wstring_view sv, const wchar_t* t)
{
    return sv < wstring_view(t);
}

inline bool operator<=(const wchar_t* t, wstring_view sv)
{
    return wstring_view(t) <= sv;
}

inline bool operator<=(wstring_view sv, const wchar_t* t)
{
    return sv <= wstring_view(t);
}

inline bool operator>(const wchar_t* t, wstring_view sv)
{
    return wstring_view(t) > sv;
}

inline bool operator>(wstring_view sv, const wchar_t* t)
{
    return sv > wstring_view(t);
}

inline bool operator>=(const wchar_t* t, wstring_view sv)
{
    return wstring_view(t) >= sv;
}

inline bool operator>=(wstring_view sv, const wchar_t* t)
{
    return sv >= wstring_view(t);
}

#if CARB_HAS_CPP20 && defined(__cpp_char8_t)
inline bool operator==(const char8_t* t, u8string_view sv)
{
    return u8string_view(t) == sv;
}

inline bool operator==(u8string_view sv, const char8_t* t)
{
    return sv == u8string_view(t);
}

inline bool operator!=(const char8_t* t, u8string_view sv)
{
    return u8string_view(t) != sv;
}

inline bool operator!=(u8string_view sv, const char8_t* t)
{
    return sv != u8string_view(t);
}

inline bool operator<(const char8_t* t, u8string_view sv)
{
    return u8string_view(t) < sv;
}

inline bool operator<(u8string_view sv, const char8_t* t)
{
    return sv < u8string_view(t);
}

inline bool operator<=(const char8_t* t, u8string_view sv)
{
    return u8string_view(t) <= sv;
}

inline bool operator<=(u8string_view sv, const char8_t* t)
{
    return sv <= u8string_view(t);
}

inline bool operator>(const char8_t* t, u8string_view sv)
{
    return u8string_view(t) > sv;
}

inline bool operator>(u8string_view sv, const char8_t* t)
{
    return sv > u8string_view(t);
}

inline bool operator>=(const char8_t* t, u8string_view sv)
{
    return u8string_view(t) >= sv;
}

inline bool operator>=(u8string_view sv, const char8_t* t)
{
    return sv >= u8string_view(t);
}
#endif

inline bool operator==(const char16_t* t, u16string_view sv)
{
    return u16string_view(t) == sv;
}

inline bool operator==(u16string_view sv, const char16_t* t)
{
    return sv == u16string_view(t);
}

inline bool operator!=(const char16_t* t, u16string_view sv)
{
    return u16string_view(t) != sv;
}

inline bool operator!=(u16string_view sv, const char16_t* t)
{
    return sv != u16string_view(t);
}

inline bool operator<(const char16_t* t, u16string_view sv)
{
    return u16string_view(t) < sv;
}

inline bool operator<(u16string_view sv, const char16_t* t)
{
    return sv < u16string_view(t);
}

inline bool operator<=(const char16_t* t, u16string_view sv)
{
    return u16string_view(t) <= sv;
}

inline bool operator<=(u16string_view sv, const char16_t* t)
{
    return sv <= u16string_view(t);
}

inline bool operator>(const char16_t* t, u16string_view sv)
{
    return u16string_view(t) > sv;
}

inline bool operator>(u16string_view sv, const char16_t* t)
{
    return sv > u16string_view(t);
}

inline bool operator>=(const char16_t* t, u16string_view sv)
{
    return u16string_view(t) >= sv;
}

inline bool operator>=(u16string_view sv, const char16_t* t)
{
    return sv >= u16string_view(t);
}

inline bool operator==(const char32_t* t, u32string_view sv)
{
    return u32string_view(t) == sv;
}

inline bool operator==(u32string_view sv, const char32_t* t)
{
    return sv == u32string_view(t);
}

inline bool operator!=(const char32_t* t, u32string_view sv)
{
    return u32string_view(t) != sv;
}

inline bool operator!=(u32string_view sv, const char32_t* t)
{
    return sv != u32string_view(t);
}

inline bool operator<(const char32_t* t, u32string_view sv)
{
    return u32string_view(t) < sv;
}

inline bool operator<(u32string_view sv, const char32_t* t)
{
    return sv < u32string_view(t);
}

inline bool operator<=(const char32_t* t, u32string_view sv)
{
    return u32string_view(t) <= sv;
}

inline bool operator<=(u32string_view sv, const char32_t* t)
{
    return sv <= u32string_view(t);
}

inline bool operator>(const char32_t* t, u32string_view sv)
{
    return u32string_view(t) > sv;
}

inline bool operator>(u32string_view sv, const char32_t* t)
{
    return sv > u32string_view(t);
}

inline bool operator>=(const char32_t* t, u32string_view sv)
{
    return u32string_view(t) >= sv;
}

inline bool operator>=(u32string_view sv, const char32_t* t)
{
    return sv >= u32string_view(t);
}

// Note that literal suffixes that don't start with _ are reserved, in addition we probably don't want to compete with
// the C++17 suffix either.
constexpr string_view operator""_sv(const char* str, std::size_t len) noexcept
{
    return string_view(str, len);
}

// C++ 20 and above have char8_t.
#if CARB_HAS_CPP20 && defined(__cpp_char8_t)
constexpr u8string_view operator""_sv(const char8_t* str, std::size_t len) noexcept
{
    return u8string_view(str, len);
}
#endif

constexpr u16string_view operator""_sv(const char16_t* str, std::size_t len) noexcept
{
    return u16string_view(str, len);
}

constexpr u32string_view operator""_sv(const char32_t* str, std::size_t len) noexcept
{
    return u32string_view(str, len);
}

constexpr wstring_view operator""_sv(const wchar_t* str, std::size_t len) noexcept
{
    return wstring_view(str, len);
}

// Non-standard function?
template <class CharT, class Traits>
constexpr void swap(carb::cpp::basic_string_view<CharT, Traits>& a, carb::cpp::basic_string_view<CharT, Traits>& b) noexcept
{
    a.swap(b);
};

} // namespace cpp
} // namespace carb

template <class CharT, class Traits>
struct std::hash<carb::cpp::basic_string_view<CharT, Traits>>
{
    size_t operator()(const carb::cpp::basic_string_view<CharT, Traits>& v) const
    {
        return carb::hashBuffer(v.data(), (uintptr_t)(v.data() + v.size()) - (uintptr_t)(v.data()));
    }
};

#undef CARB_ALWAYS_FAIL
#undef CARB_THROW_OR_CHECK