548 lines
16 KiB
C++
548 lines
16 KiB
C++
// sv.h
|
|
//
|
|
// Copyright (C) 2021-present, Celestia Development Team.
|
|
//
|
|
// Read-only view of C/C++ strings.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
|
|
#pragma once
|
|
|
|
#include <string>
|
|
#include <stdexcept>
|
|
#include <algorithm>
|
|
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
// M$VC++ build without C++ exceptions are not supported yet
|
|
#ifndef __cpp_exceptions
|
|
#define __cpp_exceptions 1
|
|
#endif
|
|
#endif
|
|
|
|
#if ! __cpp_exceptions
|
|
#include <cstdlib>
|
|
#endif
|
|
|
|
namespace celestia
|
|
{
|
|
namespace compat
|
|
{
|
|
template<
|
|
typename CharT,
|
|
typename Traits = std::char_traits<CharT>
|
|
> class 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 = const CharT*;
|
|
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;
|
|
|
|
enum : 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_size { count }
|
|
{}
|
|
constexpr basic_string_view(const std::basic_string<CharT, Traits> &s) :
|
|
m_data { s.data() },
|
|
m_size { s.size() }
|
|
{}
|
|
|
|
constexpr basic_string_view(const CharT* s) :
|
|
m_data { s },
|
|
m_size { Traits::length(s) }
|
|
{}
|
|
|
|
constexpr basic_string_view& operator=(const basic_string_view&) noexcept = default;
|
|
|
|
explicit operator std::basic_string<CharT, Traits>() const
|
|
{
|
|
return { m_data, m_size };
|
|
}
|
|
|
|
constexpr const_pointer data() const noexcept
|
|
{
|
|
return m_data;
|
|
}
|
|
constexpr size_type size() const noexcept
|
|
{
|
|
return m_size;
|
|
}
|
|
constexpr size_type length() const noexcept
|
|
{
|
|
return size();
|
|
}
|
|
constexpr bool empty() const noexcept
|
|
{
|
|
return size() == 0;
|
|
}
|
|
constexpr const_reference operator[](size_type pos) const
|
|
{
|
|
return m_data[pos];
|
|
}
|
|
constexpr const_reference at(size_type pos) const
|
|
{
|
|
if (pos < size())
|
|
return m_data[pos];
|
|
#if __cpp_exceptions
|
|
throw std::out_of_range("pos >= size()");
|
|
#else
|
|
std::abort();
|
|
#endif
|
|
}
|
|
constexpr const_reference front() const
|
|
{
|
|
return *m_data;
|
|
}
|
|
constexpr const_reference back() const
|
|
{
|
|
return *(m_data + m_size - 1);
|
|
}
|
|
constexpr size_type max_size() const noexcept
|
|
{
|
|
return (npos - sizeof(size_type) - sizeof(pointer)) / sizeof(value_type) / 4;
|
|
}
|
|
constexpr const_iterator begin() const noexcept
|
|
{
|
|
return m_data;
|
|
}
|
|
constexpr const_iterator cbegin() const noexcept
|
|
{
|
|
return begin();
|
|
}
|
|
constexpr const_iterator end() const noexcept
|
|
{
|
|
return m_data + m_size;
|
|
}
|
|
constexpr const_iterator cend() const noexcept
|
|
{
|
|
return end();
|
|
}
|
|
constexpr const_reverse_iterator rbegin() const noexcept
|
|
{
|
|
return const_reverse_iterator(end());
|
|
}
|
|
constexpr const_reverse_iterator crbegin() const noexcept
|
|
{
|
|
return const_reverse_iterator(end());
|
|
}
|
|
constexpr const_reverse_iterator rend() const noexcept
|
|
{
|
|
return const_reverse_iterator(begin());
|
|
}
|
|
constexpr const_reverse_iterator crend() const noexcept
|
|
{
|
|
return const_reverse_iterator(begin());
|
|
}
|
|
constexpr void remove_prefix(size_type n)
|
|
{
|
|
m_data += n;
|
|
m_size -= n;
|
|
}
|
|
constexpr void remove_suffix(size_type n)
|
|
{
|
|
m_size -= n;
|
|
}
|
|
constexpr void swap(basic_string_view& v) noexcept
|
|
{
|
|
auto t = *this;
|
|
this = v;
|
|
v = t;
|
|
}
|
|
size_type copy(CharT* dest, size_type count, size_type pos = 0) const
|
|
{
|
|
if (pos > m_size)
|
|
#if __cpp_exceptions
|
|
throw std::out_of_range("pos >= size()");
|
|
#else
|
|
std::abort();
|
|
#endif
|
|
|
|
auto rcount = std::min(count, m_size - pos);
|
|
return Traits::copy(dest, m_data + pos, rcount);
|
|
}
|
|
constexpr basic_string_view substr(size_type pos = 0, size_type count = npos ) const
|
|
{
|
|
if (pos > m_size)
|
|
#if __cpp_exceptions
|
|
throw std::out_of_range("pos >= size()");
|
|
#else
|
|
std::abort();
|
|
#endif
|
|
|
|
auto rcount = std::min(count, m_size - pos);
|
|
return { m_data + pos, rcount };
|
|
}
|
|
constexpr int compare(basic_string_view v) const noexcept
|
|
{
|
|
auto r = Traits::compare(data(), v.data(), std::min(size(), v.size()));
|
|
return r == 0 ? size() - v.size() : r;
|
|
}
|
|
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 size_type find(basic_string_view v, size_type pos = 0) const noexcept
|
|
{
|
|
return (pos >= m_size ? npos : (basic_string_view(m_data + pos, v.size()).compare(v) == 0 ? pos : find(v, pos + 1)));
|
|
}
|
|
constexpr size_type find(CharT ch, size_type pos = 0) const noexcept
|
|
{
|
|
return find(basic_string_view(std::addressof(ch), 1), pos);
|
|
}
|
|
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;
|
|
if (v.size() == 0)
|
|
return std::min(size(), pos);
|
|
auto last = begin() + std::min(pos, size());
|
|
auto r = std::find_end(begin(), last, v.begin(), v.end());
|
|
if (r != last)
|
|
return r - begin();
|
|
return npos;
|
|
}
|
|
constexpr size_type rfind(CharT c, size_type pos = npos) const noexcept
|
|
{
|
|
return rfind(basic_string_view(std::addressof(c), 1), pos);
|
|
}
|
|
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
|
|
{
|
|
for (size_type i = pos; i < m_size; i++)
|
|
for (auto c : v)
|
|
if (c == m_data[i])
|
|
return i;
|
|
return npos;
|
|
}
|
|
constexpr size_type find_first_of(CharT c, size_type pos = 0) const noexcept
|
|
{
|
|
return find_first_of(basic_string_view(std::addressof(c), 1), 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
|
|
{
|
|
for (auto iter = cbegin() + std::min(m_size - 1, pos); iter >= cbegin(); iter--)
|
|
for (auto c : v)
|
|
if (c == *iter)
|
|
return iter - cbegin();
|
|
return npos;
|
|
}
|
|
constexpr size_type find_last_of(CharT c, size_type pos = npos) const noexcept
|
|
{
|
|
return find_last_of(basic_string_view(std::addressof(c), 1), 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
|
|
{
|
|
for (size_type i = pos; i < m_size; i++)
|
|
if (v.find(m_data[i]) == npos)
|
|
return i;
|
|
return npos;
|
|
}
|
|
constexpr size_type find_first_not_of(CharT c, size_type pos = 0) const noexcept
|
|
{
|
|
return find_first_not_of(basic_string_view(std::addressof(c), 1), pos);
|
|
}
|
|
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 = npos) const noexcept
|
|
{
|
|
for (auto iter = cbegin() + std::min(m_size - 1, pos); iter >= cbegin(); iter--)
|
|
if (v.find(*iter) == npos)
|
|
return iter - cbegin();
|
|
return npos;
|
|
}
|
|
constexpr size_type find_last_not_of(CharT c, size_type pos = npos) const noexcept
|
|
{
|
|
return find_last_not_of(basic_string_view(std::addressof(c), 1), pos);
|
|
}
|
|
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 = npos) const
|
|
{
|
|
return find_last_not_of(basic_string_view(s), pos);
|
|
}
|
|
|
|
private:
|
|
const_pointer m_data { nullptr };
|
|
size_type m_size { 0 };
|
|
};
|
|
|
|
using string_view = basic_string_view<char>;
|
|
using wstring_view = basic_string_view<wchar_t>;
|
|
|
|
template<class CharT, class Traits>
|
|
constexpr bool operator==(basic_string_view <CharT, Traits> lhs,
|
|
basic_string_view <CharT, Traits> rhs) noexcept
|
|
{
|
|
return lhs.compare(rhs) == 0;
|
|
}
|
|
|
|
/*!
|
|
* Helpers missing in C++17 for compatibility with C++11/C++14
|
|
*/
|
|
template<class CharT, class Traits>
|
|
constexpr bool operator==(basic_string_view <CharT, Traits> lhs,
|
|
std::basic_string <CharT, Traits> rhs) noexcept
|
|
{
|
|
return lhs == basic_string_view<CharT, Traits>(rhs);
|
|
}
|
|
template<class CharT, class Traits>
|
|
constexpr bool operator==(std::basic_string <CharT, Traits> lhs,
|
|
basic_string_view <CharT, Traits> rhs) noexcept
|
|
{
|
|
return rhs == lhs;
|
|
}
|
|
template<class CharT, class Traits, size_t N>
|
|
constexpr bool operator==(basic_string_view <CharT, Traits> lhs,
|
|
const CharT (&rhs)[N]) noexcept
|
|
{
|
|
return lhs == basic_string_view<CharT, Traits>(rhs, N-1);
|
|
}
|
|
template<class CharT, class Traits, size_t N>
|
|
constexpr bool operator==(const CharT (&lhs)[N],
|
|
basic_string_view <CharT, Traits> rhs) noexcept
|
|
{
|
|
return rhs == lhs;
|
|
}
|
|
template<class CharT, class Traits, size_t N>
|
|
constexpr bool operator==(basic_string_view <CharT, Traits> lhs,
|
|
const CharT* rhs) noexcept
|
|
{
|
|
return lhs.compare(rhs) == 0;
|
|
}
|
|
template<class CharT, class Traits, size_t N>
|
|
constexpr bool operator==(const CharT* lhs,
|
|
basic_string_view <CharT, Traits> rhs) noexcept
|
|
{
|
|
return rhs == lhs;
|
|
}
|
|
|
|
template<class CharT, class Traits>
|
|
constexpr bool operator!=(basic_string_view <CharT, Traits> lhs,
|
|
basic_string_view <CharT, Traits> rhs) noexcept
|
|
{
|
|
return lhs.compare(rhs) != 0;
|
|
}
|
|
|
|
/*!
|
|
* Helpers missing in C++17 for compatibility with C++11/C++14
|
|
*/
|
|
template<class CharT, class Traits>
|
|
constexpr bool operator!=(basic_string_view <CharT, Traits> lhs,
|
|
std::basic_string <CharT, Traits> rhs) noexcept
|
|
{
|
|
return !(lhs == rhs);
|
|
}
|
|
template<class CharT, class Traits>
|
|
constexpr bool operator!=(std::basic_string <CharT, Traits> lhs,
|
|
basic_string_view <CharT, Traits> rhs) noexcept
|
|
{
|
|
return !(lhs == rhs);
|
|
}
|
|
template<class CharT, class Traits, size_t N>
|
|
constexpr bool operator!=(basic_string_view <CharT, Traits> lhs,
|
|
const CharT* rhs) noexcept
|
|
{
|
|
return !(lhs == rhs);
|
|
}
|
|
template<class CharT, class Traits, size_t N>
|
|
constexpr bool operator!=(const CharT* lhs,
|
|
basic_string_view <CharT, Traits> rhs) noexcept
|
|
{
|
|
return !(lhs == rhs);
|
|
}
|
|
|
|
template<class CharT, class Traits>
|
|
constexpr bool operator<(basic_string_view <CharT, Traits> lhs,
|
|
basic_string_view <CharT, Traits> rhs) noexcept
|
|
{
|
|
return lhs.compare(rhs) < 0;
|
|
}
|
|
template<class CharT, class Traits>
|
|
constexpr bool operator<=(basic_string_view <CharT, Traits> lhs,
|
|
basic_string_view <CharT, Traits> rhs) noexcept
|
|
{
|
|
return lhs.compare(rhs) <= 0;
|
|
}
|
|
template<class CharT, class Traits>
|
|
constexpr bool operator>(basic_string_view <CharT, Traits> lhs,
|
|
basic_string_view <CharT, Traits> rhs) noexcept
|
|
{
|
|
return lhs.compare(rhs) > 0;
|
|
}
|
|
template<class CharT, class Traits>
|
|
constexpr bool operator>=(basic_string_view <CharT, Traits> lhs,
|
|
basic_string_view <CharT, Traits> rhs) noexcept
|
|
{
|
|
return lhs.compare(rhs) >= 0;
|
|
}
|
|
|
|
template <class CharT, class Traits>
|
|
std::basic_ostream <CharT, Traits>& operator<<(std::basic_ostream <CharT, Traits>& os,
|
|
celestia::compat::basic_string_view <CharT, Traits> v)
|
|
{
|
|
os.write(v.data(), v.size());
|
|
return os;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
template<size_t size = sizeof(size_t)>
|
|
struct fnv;
|
|
|
|
template<>
|
|
struct fnv<4>
|
|
{
|
|
enum : uint32_t
|
|
{
|
|
offset = 2166136261ul,
|
|
prime = 16777619ul
|
|
};
|
|
};
|
|
|
|
template<>
|
|
struct fnv<8>
|
|
{
|
|
enum : uint64_t
|
|
{
|
|
offset = 14695981039346656037ull,
|
|
prime = 1099511628211ull
|
|
};
|
|
};
|
|
|
|
constexpr size_t fnv1a_loop_helper(const char *begin, const char *end, size_t hash = fnv<>::offset)
|
|
{
|
|
return begin < end ? fnv1a_loop_helper(begin + 1, end, (hash ^ size_t(*begin)) * fnv<>::prime) : hash;
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
struct hash;
|
|
|
|
template<>
|
|
struct hash<string_view>
|
|
{
|
|
size_t operator()(const string_view& sv) const noexcept
|
|
{
|
|
#if 0
|
|
size_t _hash = fnv<>::offset;
|
|
for (char c : sv)
|
|
{
|
|
_hash ^= size_t(c);
|
|
_hash *= fnv<>::prime;
|
|
}
|
|
return _hash;
|
|
#else
|
|
return fnv1a_loop_helper(sv.data(), sv.data() + sv.length());
|
|
#endif
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
#if defined(__clang__)
|
|
#pragma clang diagnostic ignored "-Wreserved-user-defined-literal"
|
|
//#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wuser-defined-literals"
|
|
#elif defined(__GNUC__)
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wliteral-suffix"
|
|
#endif
|
|
constexpr celestia::compat::string_view operator "" sv(const char *str, std::size_t len)
|
|
{
|
|
return { str, len };
|
|
}
|
|
|
|
constexpr celestia::compat::wstring_view operator "" sv(const wchar_t *str, std::size_t len)
|
|
{
|
|
return { str, len };
|
|
}
|
|
|
|
constexpr celestia::compat::string_view operator "" _sv(const char *str, std::size_t len)
|
|
{
|
|
return { str, len };
|
|
}
|
|
|
|
constexpr celestia::compat::wstring_view operator "" _sv(const wchar_t *str, std::size_t len)
|
|
{
|
|
return { str, len };
|
|
}
|
|
|
|
#if defined(__clang__)
|
|
//#pragma clang diagnostic pop
|
|
#elif defined(__GNUC__)
|
|
#pragma GCC diagnostic pop
|
|
#endif
|