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