ZStringView.h#
Fully qualified name: carb/cpp/ZStringView.h
File members: carb/cpp/ZStringView.h
// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: LicenseRef-NvidiaProprietary
//
// NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
// property and proprietary rights in and to this material, related
// documentation and any modifications thereto. Any use, reproduction,
// disclosure or distribution of this material and related documentation
// without an express license agreement from NVIDIA CORPORATION or
// its affiliates is strictly prohibited.
#pragma once
#include "../Defines.h"
#include "../../omni/detail/PointerIterator.h"
#include "TypeTraits.h"
#include "Memory.h"
#include "StringView.h"
#include <cstddef>
#include <iterator>
#include <string> // for std::string and std::hash
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace carb
{
namespace cpp
{
template <class CharT, class Traits>
class basic_zstring_view;
// 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.
#ifndef DOXYGEN_BUILD
# 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
#endif
template <class CharT, class Traits = std::char_traits<CharT>>
class CARB_VIZ basic_zstring_view
{
static_assert(!std::is_array_v<CharT>, "CharT must not be an array type");
static_assert(std::is_trivial_v<CharT> && std::is_standard_layout_v<CharT>,
"CharT must be trivial and standard layout");
static_assert(std::is_same_v<CharT, typename Traits::char_type>, "CharT and Traits::char_type must be the same");
static inline constexpr CharT nullTerminator = CharT{};
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_zstring_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_zstring_view() noexcept = default;
constexpr basic_zstring_view(const basic_zstring_view& other) noexcept = default;
constexpr basic_zstring_view(const CharT* s, size_type count) : m_data(s ? s : &nullTerminator), m_count(count)
{
CARB_THROW_OR_CHECK(traits_type::eq(m_data[count], CharT{}), "no null terminator at count");
}
template <size_t N>
constexpr basic_zstring_view(const CharT (&s)[N]) : m_data(s), m_count(N - 1)
{
for (size_type i = 0; i < N - 1; ++i)
{
if (traits_type::eq(s[i], CharT{}))
{
m_count = i;
return;
}
}
CARB_THROW_OR_CHECK(traits_type::eq(s[m_count], CharT{}), "no null terminator");
}
constexpr basic_zstring_view(unsafe_length_t, const CharT* s) noexcept
: m_data(s ? s : &nullTerminator), m_count(s ? traits_type::length(s) : 0)
{
}
template <class It CARB_NO_DOC(
,
std::enable_if_t<std::is_same_v<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag> &&
!detail::IsReverseIterator_v<It>,
bool> = false)>
constexpr basic_zstring_view(It, It) = delete;
template <class R CARB_NO_DOC(
,
std::enable_if_t<cpp::detail::IsConvertibleRange_v<remove_cvref_t<R>, const_pointer> &&
!detail::IsBasicStringView_v<remove_cvref_t<R>> && !is_bounded_array_v<remove_cvref_t<R>> &&
!detail::IsStdBasicString_v<remove_cvref_t<R>> && !std::is_convertible_v<R, const CharT*> &&
!detail::HasOperator_v<remove_cvref_t<R>, basic_string_view<CharT, Traits>> &&
!detail::HasOperator_v<remove_cvref_t<R>, basic_zstring_view>,
bool> = false)>
constexpr explicit basic_zstring_view(R&&) = delete;
template <typename T,
typename = std::enable_if_t<!std::is_pointer_v<T> && std::is_same_v<T, std::basic_string<CharT, Traits>>>>
constexpr basic_zstring_view(const T& s) : m_data(s.data()), m_count(s.size())
{
}
constexpr basic_zstring_view(std::nullptr_t) = delete;
constexpr basic_zstring_view& operator=(const basic_zstring_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 const_pointer c_str() 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;
}
[[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 swap(basic_zstring_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_zstring_view substr(size_t pos) const
{
CARB_THROW_OR_CHECK(pos <= size(), "pos > size()");
return { m_data + pos, size() - pos };
}
constexpr basic_string_view<CharT, Traits> substr(size_t pos, size_t count) 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<CharT, Traits> 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;
}
template <class T,
std::enable_if_t<!is_bounded_array_v<remove_cvref_t<T>> && std::is_convertible_v<T, const_pointer>, bool> = false>
constexpr int compare(T&& s) const noexcept
{
const_pointer terminator = traits_type::find(std::forward<T>(s), size(), CharT{});
size_type rlen = terminator ? size_type(terminator - s) : size();
int result = traits_type::compare(data(), std::forward<T>(s), rlen);
if (result == 0)
{
if (rlen < size())
return 1;
else if (traits_type::eq(s[rlen], CharT{}))
return 0;
else
return -1;
}
return result;
}
constexpr int compare(size_type pos1, size_type count1, basic_string_view<CharT, Traits> v) const
{
return substr(pos1, count1).compare(v);
}
constexpr int compare(size_type pos1, size_type count1, const CharT* s) const
{
return substr(pos1, count1).compare(s);
}
constexpr int compare(
size_type pos1, size_type count1, basic_string_view<CharT, Traits> v, size_type pos2, size_type count2) const
{
return substr(pos1, count1).compare(v.substr(pos2, count2));
}
constexpr int compare(size_type pos1, size_type count1, const CharT* s, size_type count2) const
{
return substr(pos1, count1).compare(basic_string_view<CharT, Traits>(s, count2));
}
constexpr bool starts_with(basic_string_view<CharT, Traits> sv) const noexcept
{
return substr(0, sv.size()) == sv;
}
constexpr bool starts_with(CharT ch) const noexcept
{
return !empty() && traits_type::eq(front(), ch);
}
template <class T,
std::enable_if_t<!is_bounded_array_v<remove_cvref_t<T>> && std::is_convertible_v<T, const_pointer>, bool> = false>
constexpr bool starts_with(T&& s) const noexcept
{
size_type i = 0;
while (i < size())
{
if (traits_type::eq(s[i], CharT{}))
{
return true;
}
if (!traits_type::eq(s[i], m_data[i]))
{
return false;
}
++i;
}
return traits_type::eq(s[i], CharT{});
}
constexpr bool ends_with(basic_string_view<CharT, Traits> 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);
}
template <class T,
std::enable_if_t<!is_bounded_array_v<remove_cvref_t<T>> && std::is_convertible_v<T, const_pointer>, bool> = false>
constexpr bool ends_with(T&& s) const noexcept
{
const CharT* end = traits_type::find(std::forward<T>(s), size() + 1, CharT{});
if (!end)
return false;
auto suffix_size = size_type(end - s);
return traits_type::compare(m_data + size() - suffix_size, std::forward<T>(s), suffix_size) == 0;
}
constexpr bool contains(basic_string_view<CharT, Traits> sv) const noexcept
{
return find(sv) != npos;
}
constexpr bool contains(CharT c) const noexcept
{
return find(c) != npos;
}
template <class T,
std::enable_if_t<!is_bounded_array_v<remove_cvref_t<T>> && std::is_convertible_v<T, const_pointer>, bool> = false>
constexpr bool contains(T&& s) const noexcept
{
return find(std::forward<T>(s)) != npos;
}
constexpr size_type find(basic_string_view<CharT, Traits> v, size_type pos = 0) const noexcept
{
for (size_type xpos = pos; xpos + v.size() <= size(); ++xpos)
{
if (substr(xpos).starts_with(v))
{
return xpos;
}
}
return npos;
}
constexpr size_type find(CharT ch, size_type pos = 0) const noexcept
{
for (size_type xpos = pos; xpos < size(); ++xpos)
{
if (traits_type::eq(m_data[xpos], ch))
{
return xpos;
}
}
return npos;
}
constexpr size_type find(const CharT* s, size_type pos, size_type count) const noexcept
{
return find(basic_string_view<CharT, Traits>(s, count), pos);
}
template <class T,
std::enable_if_t<!is_bounded_array_v<remove_cvref_t<T>> && std::is_convertible_v<T, const_pointer>, bool> = false>
constexpr size_type find(T&& s, size_type pos = 0) const noexcept
{
// NOTE: We really want to have <= here, because it will handle the case where s is an empty string.
for (size_type xpos = pos; xpos <= size(); ++xpos)
{
if (substr(xpos).starts_with(std::forward<T>(s)))
{
return xpos;
}
}
return npos;
}
constexpr size_type rfind(basic_string_view<CharT, Traits> 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 (substr(xpos).starts_with(v))
{
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, m_data[xpos]))
{
return xpos;
}
if (xpos == 0)
{
break;
}
}
return npos;
}
constexpr size_type rfind(const CharT* s, size_type pos, size_type count) const noexcept
{
return rfind(basic_string_view<CharT, Traits>(s, count), pos);
}
template <class T,
std::enable_if_t<!is_bounded_array_v<remove_cvref_t<T>> && std::is_convertible_v<T, const_pointer>, bool> = false>
constexpr size_type rfind(T&& s, size_type pos = npos) const noexcept
{
// Clip the position to our string length.
for (size_type xpos = ::carb_min(pos, size());; xpos--)
{
if (substr(xpos).starts_with(std::forward<T>(s)))
{
return xpos;
}
if (xpos == 0)
{
break;
}
}
return npos;
}
constexpr size_type find_first_of(basic_string_view<CharT, Traits> v, size_type pos = 0) const noexcept
{
if (v.empty())
{
return npos;
}
for (size_type xpos = pos; xpos < size(); ++xpos)
{
if (v.find(m_data[xpos]) != npos)
{
return 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 noexcept
{
return find_first_of(basic_string_view<CharT, Traits>(s, count), pos);
}
constexpr size_type find_first_of(unsafe_length_t, const CharT* s, size_type pos = 0) const noexcept
{
return find_first_of(basic_string_view<CharT, Traits>(unsafe_length, s), pos);
}
#ifndef DOXYGEN_BUILD
template <class T,
std::enable_if_t<!is_bounded_array_v<remove_cvref_t<T>> && std::is_convertible_v<T, const_pointer>, bool> = false>
constexpr size_type find_first_of(T&& s, size_type pos = 0) const = delete;
#endif
constexpr size_type find_last_of(basic_string_view<CharT, Traits> 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(m_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 noexcept
{
return find_last_of(basic_string_view<CharT, Traits>(s, count), pos);
}
constexpr size_type find_last_of(unsafe_length_t, const CharT* s, size_type pos = npos) const noexcept
{
return find_last_of(basic_string_view<CharT, Traits>(unsafe_length, s), pos);
}
#ifndef DOXYGEN_BUILD
template <class T,
std::enable_if_t<!is_bounded_array_v<remove_cvref_t<T>> && std::is_convertible_v<T, const_pointer>, bool> = false>
constexpr size_type find_last_of(T&& s, size_type pos = npos) const = delete;
#endif
constexpr size_type find_first_not_of(basic_string_view<CharT, Traits> v, size_type pos = 0) const noexcept
{
for (size_type xpos = pos; xpos < size(); ++xpos)
{
if (v.find(m_data[xpos]) == npos)
{
return 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 noexcept
{
return find_first_not_of(basic_string_view<CharT, Traits>(s, count), pos);
}
constexpr size_type find_first_not_of(unsafe_length_t, const CharT* s, size_type pos = 0) const noexcept
{
return find_first_not_of(basic_string_view<CharT, Traits>(unsafe_length, s), pos);
}
#ifndef DOXYGEN_BUILD
template <class T,
std::enable_if_t<!is_bounded_array_v<remove_cvref_t<T>> && std::is_convertible_v<T, const_pointer>, bool> = false>
constexpr size_type find_first_not_of(T&& s, size_type pos = 0) const = delete;
#endif
constexpr size_type find_last_not_of(basic_string_view<CharT, Traits> 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(m_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(m_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 noexcept
{
return find_last_not_of(basic_string_view<CharT, Traits>(s, count), pos);
}
constexpr size_type find_last_not_of(unsafe_length_t, const CharT* s, size_type pos = 0) const noexcept
{
return find_last_not_of(basic_string_view<CharT, Traits>(unsafe_length, s), pos);
}
#ifndef DOXYGEN_BUILD
template <class T,
std::enable_if_t<!is_bounded_array_v<remove_cvref_t<T>> && std::is_convertible_v<T, const_pointer>, bool> = false>
constexpr size_type find_last_not_of(T&& s, size_type pos = 0) const = delete;
#endif
operator std::basic_string_view<CharT, Traits>() const noexcept
{
return { m_data, m_count };
}
operator basic_string_view<CharT, Traits>() const noexcept
{
return { m_data, m_count };
}
private:
CARB_VIZ const_pointer m_data = &nullTerminator;
CARB_VIZ size_type m_count = 0;
};
using zstring_view = basic_zstring_view<char>;
using wzstring_view = basic_zstring_view<wchar_t>;
#if CARB_HAS_CPP20 && defined(__cpp_char8_t)
using u8zstring_view = basic_zstring_view<char8_t>;
#endif
using u16zstring_view = basic_zstring_view<char16_t>;
using u32zstring_view = basic_zstring_view<char32_t>;
// Ensure these for ABI safety
#define CARB_IMPL_ENSURE_ABI(cls) \
static_assert(std::is_standard_layout_v<cls>, #cls " must be standard layout"); \
static_assert(std::is_trivially_copyable_v<cls>, #cls " must be trivially copyable"); /* C++23 requirement */ \
static_assert(sizeof(cls) == (2 * sizeof(size_t)), #cls "ABI change violation")
CARB_IMPL_ENSURE_ABI(zstring_view);
CARB_IMPL_ENSURE_ABI(wzstring_view);
#if CARB_HAS_CPP20 && defined(__cpp_char8_t)
CARB_IMPL_ENSURE_ABI(u8string_view);
#endif
CARB_IMPL_ENSURE_ABI(u16zstring_view);
CARB_IMPL_ENSURE_ABI(u32zstring_view);
#undef CARB_IMPL_ENSURE_ABI
// [string.view.comparison]
// type_identity_t is used to create a non-deduced context so that one argument participates
// in template argument deduction and the other one gets implicitly converted.
// To overcome an MSVC issue with name mangling some overloads have a dummy default parameter.
template <class CharT, class Traits>
constexpr bool operator==(basic_zstring_view<CharT, Traits> a, const CharT* b)
{
return a.compare(b) == 0;
}
template <class CharT, class Traits>
constexpr bool operator==(const CharT* a, basic_zstring_view<CharT, Traits> b)
{
return b.compare(a) == 0;
}
template <class CharT, class Traits>
constexpr bool operator!=(basic_zstring_view<CharT, Traits> a, const CharT* b)
{
return !(a == b);
}
template <class CharT, class Traits>
constexpr bool operator!=(const CharT* a, basic_zstring_view<CharT, Traits> b)
{
return !(a == b);
}
template <class CharT, class Traits>
constexpr bool operator<(basic_zstring_view<CharT, Traits> a, const CharT* b)
{
return a.compare(b) < 0;
}
template <class CharT, class Traits>
constexpr bool operator<(const CharT* a, basic_zstring_view<CharT, Traits> b)
{
return b.compare(a) > 0;
}
template <class CharT, class Traits>
constexpr bool operator>(basic_zstring_view<CharT, Traits> a, const CharT* b)
{
return b < a;
}
template <class CharT, class Traits>
constexpr bool operator>(const CharT* a, basic_zstring_view<CharT, Traits> b)
{
return b < a;
}
template <class CharT, class Traits>
constexpr bool operator<=(basic_zstring_view<CharT, Traits> a, const CharT* b)
{
return !(a > b);
}
template <class CharT, class Traits>
constexpr bool operator<=(const CharT* a, basic_zstring_view<CharT, Traits> b)
{
return !(a > b);
}
template <class CharT, class Traits>
constexpr bool operator>=(basic_zstring_view<CharT, Traits> a, const CharT* b)
{
return !(a < b);
}
template <class CharT, class Traits>
constexpr bool operator>=(const CharT* a, basic_zstring_view<CharT, Traits> b)
{
return !(a < b);
}
template <class CharT, class Traits>
constexpr bool operator==(basic_zstring_view<CharT, Traits> a, basic_zstring_view<CharT, Traits> b)
{
return a.compare(b) == 0;
}
#ifndef DOXYGEN_BUILD
template <class CharT, class Traits, int = 1>
constexpr bool operator==(basic_zstring_view<CharT, Traits> a, type_identity_t<basic_zstring_view<CharT, Traits>> b)
{
return a.compare(b) == 0;
}
#endif
#if !CARB_HAS_CPP20 || defined DOXYGEN_BUILD
template <class CharT, class Traits>
constexpr bool operator!=(basic_zstring_view<CharT, Traits> a, basic_zstring_view<CharT, Traits> b)
{
return a.compare(b) != 0;
}
template <class CharT, class Traits>
constexpr bool operator<(basic_zstring_view<CharT, Traits> a, basic_zstring_view<CharT, Traits> b)
{
return a.compare(b) < 0;
}
template <class CharT, class Traits>
constexpr bool operator>(basic_zstring_view<CharT, Traits> a, basic_zstring_view<CharT, Traits> b)
{
return a.compare(b) > 0;
}
template <class CharT, class Traits>
constexpr bool operator<=(basic_zstring_view<CharT, Traits> a, basic_zstring_view<CharT, Traits> b)
{
return a.compare(b) <= 0;
}
template <class CharT, class Traits>
constexpr bool operator>=(basic_zstring_view<CharT, Traits> a, basic_zstring_view<CharT, Traits> b)
{
return a.compare(b) >= 0;
}
# ifndef DOXYGEN_BUILD
template <class CharT, class Traits, int = 1>
constexpr bool operator!=(basic_zstring_view<CharT, Traits> a, type_identity_t<basic_zstring_view<CharT, Traits>> b)
{
return a.compare(b) != 0;
}
template <class CharT, class Traits, int = 1>
constexpr bool operator<(basic_zstring_view<CharT, Traits> a, type_identity_t<basic_zstring_view<CharT, Traits>> b)
{
return a.compare(b) < 0;
}
template <class CharT, class Traits, int = 1>
constexpr bool operator>(basic_zstring_view<CharT, Traits> a, type_identity_t<basic_zstring_view<CharT, Traits>> b)
{
return a.compare(b) > 0;
}
template <class CharT, class Traits, int = 1>
constexpr bool operator<=(basic_zstring_view<CharT, Traits> a, type_identity_t<basic_zstring_view<CharT, Traits>> b)
{
return a.compare(b) <= 0;
}
template <class CharT, class Traits, int = 1>
constexpr bool operator>=(basic_zstring_view<CharT, Traits> a, type_identity_t<basic_zstring_view<CharT, Traits>> b)
{
return a.compare(b) >= 0;
}
template <class CharT, class Traits, int = 2>
constexpr bool operator==(type_identity_t<basic_zstring_view<CharT, Traits>> a, basic_zstring_view<CharT, Traits> b)
{
return a.compare(b) == 0;
}
template <class CharT, class Traits, int = 2>
constexpr bool operator!=(type_identity_t<basic_zstring_view<CharT, Traits>> a, basic_zstring_view<CharT, Traits> b)
{
return a.compare(b) != 0;
}
template <class CharT, class Traits, int = 2>
constexpr bool operator<(type_identity_t<basic_zstring_view<CharT, Traits>> a, basic_zstring_view<CharT, Traits> b)
{
return a.compare(b) < 0;
}
template <class CharT, class Traits, int = 2>
constexpr bool operator>(type_identity_t<basic_zstring_view<CharT, Traits>> a, basic_zstring_view<CharT, Traits> b)
{
return a.compare(b) > 0;
}
template <class CharT, class Traits, int = 2>
constexpr bool operator<=(type_identity_t<basic_zstring_view<CharT, Traits>> a, basic_zstring_view<CharT, Traits> b)
{
return a.compare(b) <= 0;
}
template <class CharT, class Traits, int = 2>
constexpr bool operator>=(type_identity_t<basic_zstring_view<CharT, Traits>> a, basic_zstring_view<CharT, Traits> b)
{
return a.compare(b) >= 0;
}
# endif
#else
template <class CharT, class Traits>
constexpr auto operator<=>(const basic_zstring_view<CharT, Traits> a, const basic_zstring_view<CharT, Traits> b)
{
return a.compare(b) <=> 0;
}
template <class CharT, class Traits>
constexpr auto operator<=>(const basic_zstring_view<CharT, Traits> a, type_identity_t<basic_zstring_view<CharT, Traits>> b)
{
return a.compare(b) <=> 0;
}
template <class CharT, class Traits>
constexpr auto operator<=>(const CharT* a, const basic_zstring_view<CharT, Traits> b)
{
return -b.compare(a) <=> 0;
}
template <class CharT, class Traits>
constexpr auto operator<=>(const basic_zstring_view<CharT, Traits> a, const CharT* b)
{
return a.compare(b) <=> 0;
}
#endif
inline namespace literals
{
inline namespace string_view_literals
{
// 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 zstring_view operator""_zsv(const char* str, std::size_t len) noexcept
{
return zstring_view(str, len);
}
// C++ 20 and above have char8_t.
#if CARB_HAS_CPP20 && defined(__cpp_char8_t)
constexpr u8zstring_view operator""_zsv(const char8_t* str, std::size_t len) noexcept
{
return u8zstring_view(str, len);
}
#endif
constexpr u16zstring_view operator""_zsv(const char16_t* str, std::size_t len) noexcept
{
return u16zstring_view(str, len);
}
constexpr u32zstring_view operator""_zsv(const char32_t* str, std::size_t len) noexcept
{
return u32zstring_view(str, len);
}
constexpr wzstring_view operator""_zsv(const wchar_t* str, std::size_t len) noexcept
{
return wzstring_view(str, len);
}
} // namespace string_view_literals
} // namespace literals
} // namespace cpp
} // namespace carb
template <class CharT, class Traits>
struct std::hash<carb::cpp::basic_zstring_view<CharT, Traits>>
{
size_t operator()(const carb::cpp::basic_zstring_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